mirror of https://github.com/apache/cloudstack.git
Merge branch 'main' of https://github.com/apache/cloudstack into clvm-enhancements
This commit is contained in:
commit
be2994db0f
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
/plugins/storage/volume/linstor @rp-
|
||||
/plugins/storage/volume/storpool @slavkap
|
||||
/plugins/storage/volume/ontap @rajiv1 @sandeeplocharla @piyush5 @suryag
|
||||
|
||||
.pre-commit-config.yaml @jbampton
|
||||
/.github/linters/ @jbampton
|
||||
|
|
|
|||
|
|
@ -71,7 +71,6 @@ import org.apache.cloudstack.api.command.user.vm.GetVMPasswordCmd;
|
|||
import org.apache.cloudstack.api.command.user.vmgroup.UpdateVMGroupCmd;
|
||||
import org.apache.cloudstack.config.Configuration;
|
||||
import org.apache.cloudstack.config.ConfigurationGroup;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
|
||||
import com.cloud.alert.Alert;
|
||||
import com.cloud.capacity.Capacity;
|
||||
|
|
@ -108,14 +107,6 @@ import com.cloud.vm.VirtualMachineProfile;
|
|||
public interface ManagementService {
|
||||
static final String Name = "management-server";
|
||||
|
||||
ConfigKey<Boolean> JsInterpretationEnabled = new ConfigKey<>("Hidden"
|
||||
, Boolean.class
|
||||
, "js.interpretation.enabled"
|
||||
, "false"
|
||||
, "Enable/Disable all JavaScript interpretation related functionalities to create or update Javascript rules."
|
||||
, false
|
||||
, ConfigKey.Scope.Global);
|
||||
|
||||
/**
|
||||
* returns the a map of the names/values in the configuration table
|
||||
*
|
||||
|
|
@ -534,6 +525,4 @@ public interface ManagementService {
|
|||
|
||||
boolean removeManagementServer(RemoveManagementServerCmd cmd);
|
||||
|
||||
void checkJsInterpretationAllowedIfNeededForParameterValue(String paramName, boolean paramValue);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,6 +138,8 @@ public interface AccountService {
|
|||
|
||||
Long finalizeAccountId(String accountName, Long domainId, Long projectId, boolean enabledOnly);
|
||||
|
||||
Long finalizeAccountId(Long accountId, String accountName, Long domainId, Long projectId);
|
||||
|
||||
/**
|
||||
* returns the user account object for a given user id
|
||||
* @param userId user id
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ public class ApiConstants {
|
|||
public static final String ACCOUNT = "account";
|
||||
public static final String ACCOUNTS = "accounts";
|
||||
public static final String ACCOUNT_NAME = "accountname";
|
||||
public static final String ACCOUNT_STATE_TO_SHOW = "accountstatetoshow";
|
||||
public static final String ACCOUNT_TYPE = "accounttype";
|
||||
public static final String ACCOUNT_ID = "accountid";
|
||||
public static final String ACCOUNT_IDS = "accountids";
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ public interface VolumeImportUnmanageService extends PluggableService, Configura
|
|||
Arrays.asList(Hypervisor.HypervisorType.KVM, Hypervisor.HypervisorType.VMware);
|
||||
|
||||
List<Storage.StoragePoolType> SUPPORTED_STORAGE_POOL_TYPES_FOR_KVM = Arrays.asList(Storage.StoragePoolType.NetworkFilesystem,
|
||||
Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.RBD);
|
||||
Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.RBD, Storage.StoragePoolType.SharedMountPoint);
|
||||
|
||||
ConfigKey<Boolean> AllowImportVolumeWithBackingFile = new ConfigKey<>(Boolean.class,
|
||||
"allow.import.volume.with.backing.file",
|
||||
|
|
|
|||
|
|
@ -121,6 +121,11 @@
|
|||
<artifactId>cloud-plugin-storage-volume-adaptive</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-plugin-storage-volume-ontap</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-plugin-storage-volume-solidfire</artifactId>
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ import com.cloud.storage.StorageLayer;
|
|||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.UriUtils;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.net.HttpClientCloudStackUserAgent;
|
||||
import com.cloud.utils.net.Proxy;
|
||||
|
||||
/**
|
||||
|
|
@ -125,6 +126,7 @@ public class HttpTemplateDownloader extends ManagedContextRunnable implements Te
|
|||
GetMethod request = new GetMethod(downloadUrl);
|
||||
request.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, myretryhandler);
|
||||
request.setFollowRedirects(followRedirects);
|
||||
request.getParams().setParameter(HttpMethodParams.USER_AGENT, HttpClientCloudStackUserAgent.CLOUDSTACK_USER_AGENT);
|
||||
return request;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,11 @@
|
|||
//
|
||||
package com.cloud.storage.template;
|
||||
|
||||
|
||||
import com.cloud.storage.StorageLayer;
|
||||
import com.cloud.utils.UriUtils;
|
||||
import com.cloud.utils.net.HttpClientCloudStackUserAgent;
|
||||
|
||||
import org.apache.commons.httpclient.HttpClient;
|
||||
import org.apache.commons.httpclient.HttpMethod;
|
||||
import org.apache.commons.httpclient.HttpMethodRetryHandler;
|
||||
|
|
@ -59,6 +62,7 @@ public class MetalinkTemplateDownloader extends TemplateDownloaderBase implement
|
|||
GetMethod request = new GetMethod(downloadUrl);
|
||||
request.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, myretryhandler);
|
||||
request.setFollowRedirects(followRedirects);
|
||||
request.getParams().setParameter(HttpMethodParams.USER_AGENT, HttpClientCloudStackUserAgent.CLOUDSTACK_USER_AGENT);
|
||||
if (!toFileSet) {
|
||||
String[] parts = downloadUrl.split("/");
|
||||
String filename = parts[parts.length - 1];
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import org.apache.commons.httpclient.params.HttpMethodParams;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.cloud.storage.StorageLayer;
|
||||
import com.cloud.utils.net.HttpClientCloudStackUserAgent;
|
||||
|
||||
public class SimpleHttpMultiFileDownloader extends ManagedContextRunnable implements TemplateDownloader {
|
||||
private static final MultiThreadedHttpConnectionManager s_httpClientManager = new MultiThreadedHttpConnectionManager();
|
||||
|
|
@ -95,6 +96,7 @@ public class SimpleHttpMultiFileDownloader extends ManagedContextRunnable implem
|
|||
GetMethod request = new GetMethod(downloadUrl);
|
||||
request.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, retryHandler);
|
||||
request.setFollowRedirects(followRedirects);
|
||||
request.getParams().setParameter(HttpMethodParams.USER_AGENT, HttpClientCloudStackUserAgent.CLOUDSTACK_USER_AGENT);
|
||||
return request;
|
||||
}
|
||||
|
||||
|
|
@ -141,6 +143,7 @@ public class SimpleHttpMultiFileDownloader extends ManagedContextRunnable implem
|
|||
continue;
|
||||
}
|
||||
HeadMethod headMethod = new HeadMethod(downloadUrl);
|
||||
headMethod.getParams().setParameter(HttpMethodParams.USER_AGENT, HttpClientCloudStackUserAgent.CLOUDSTACK_USER_AGENT);
|
||||
try {
|
||||
if (client.executeMethod(headMethod) != HttpStatus.SC_OK) {
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ public class RestoreBackupCommand extends Command {
|
|||
private List<String> backupVolumesUUIDs;
|
||||
private List<PrimaryDataStoreTO> restoreVolumePools;
|
||||
private List<String> restoreVolumePaths;
|
||||
private List<Long> restoreVolumeSizes;
|
||||
private List<String> backupFiles;
|
||||
private String diskType;
|
||||
private Boolean vmExists;
|
||||
|
|
@ -92,6 +93,14 @@ public class RestoreBackupCommand extends Command {
|
|||
this.restoreVolumePaths = restoreVolumePaths;
|
||||
}
|
||||
|
||||
public List<Long> getRestoreVolumeSizes() {
|
||||
return restoreVolumeSizes;
|
||||
}
|
||||
|
||||
public void setRestoreVolumeSizes(List<Long> restoreVolumeSizes) {
|
||||
this.restoreVolumeSizes = restoreVolumeSizes;
|
||||
}
|
||||
|
||||
public List<String> getBackupFiles() {
|
||||
return backupFiles;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.apache.cloudstack.direct.download;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
|
@ -32,6 +33,7 @@ import java.util.Map;
|
|||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.UriUtils;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.net.HttpClientCloudStackUserAgent;
|
||||
import com.cloud.utils.storage.QCOW2Utils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.httpclient.HttpClient;
|
||||
|
|
@ -39,6 +41,7 @@ import org.apache.commons.httpclient.HttpStatus;
|
|||
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
|
||||
import org.apache.commons.httpclient.methods.GetMethod;
|
||||
import org.apache.commons.httpclient.methods.HeadMethod;
|
||||
import org.apache.commons.httpclient.params.HttpMethodParams;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
public class HttpDirectTemplateDownloader extends DirectTemplateDownloaderImpl {
|
||||
|
|
@ -68,6 +71,7 @@ public class HttpDirectTemplateDownloader extends DirectTemplateDownloaderImpl {
|
|||
protected GetMethod createRequest(String downloadUrl, Map<String, String> headers) {
|
||||
GetMethod request = new GetMethod(downloadUrl);
|
||||
request.setFollowRedirects(this.isFollowRedirects());
|
||||
request.getParams().setParameter(HttpMethodParams.USER_AGENT, HttpClientCloudStackUserAgent.CLOUDSTACK_USER_AGENT);
|
||||
if (MapUtils.isNotEmpty(headers)) {
|
||||
for (String key : headers.keySet()) {
|
||||
request.setRequestHeader(key, headers.get(key));
|
||||
|
|
@ -111,6 +115,7 @@ public class HttpDirectTemplateDownloader extends DirectTemplateDownloaderImpl {
|
|||
public boolean checkUrl(String url) {
|
||||
HeadMethod httpHead = new HeadMethod(url);
|
||||
httpHead.setFollowRedirects(this.isFollowRedirects());
|
||||
httpHead.getParams().setParameter(HttpMethodParams.USER_AGENT, HttpClientCloudStackUserAgent.CLOUDSTACK_USER_AGENT);
|
||||
try {
|
||||
int responseCode = client.executeMethod(httpHead);
|
||||
if (responseCode != HttpStatus.SC_OK) {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package com.cloud.vm;
|
||||
|
||||
import static com.cloud.configuration.ConfigurationManagerImpl.EXPOSE_ERRORS_TO_USER;
|
||||
import static com.cloud.configuration.ConfigurationManagerImpl.MIGRATE_VM_ACROSS_CLUSTERS;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
|
@ -941,10 +942,22 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
public void start(final String vmUuid, final Map<VirtualMachineProfile.Param, Object> params, final DeploymentPlan planToDeploy, final DeploymentPlanner planner) {
|
||||
try {
|
||||
advanceStart(vmUuid, params, planToDeploy, planner);
|
||||
} catch (ConcurrentOperationException | InsufficientCapacityException e) {
|
||||
throw new CloudRuntimeException(String.format("Unable to start a VM [%s] due to [%s].", vmUuid, e.getMessage()), e).add(VirtualMachine.class, vmUuid);
|
||||
} catch (ConcurrentOperationException e) {
|
||||
final CallContext cctxt = CallContext.current();
|
||||
final Account account = cctxt.getCallingAccount();
|
||||
if (canExposeError(account)) {
|
||||
throw new CloudRuntimeException(String.format("Unable to start a VM [%s] due to [%s].", vmUuid, e.getMessage()), e).add(VirtualMachine.class, vmUuid);
|
||||
}
|
||||
throw new CloudRuntimeException(String.format("Unable to start a VM [%s] due to concurrent operation.", vmUuid), e).add(VirtualMachine.class, vmUuid);
|
||||
} catch (final InsufficientCapacityException e) {
|
||||
final CallContext cctxt = CallContext.current();
|
||||
final Account account = cctxt.getCallingAccount();
|
||||
if (canExposeError(account)) {
|
||||
throw new CloudRuntimeException(String.format("Unable to start a VM [%s] due to [%s].", vmUuid, e.getMessage()), e).add(VirtualMachine.class, vmUuid);
|
||||
}
|
||||
throw new CloudRuntimeException(String.format("Unable to start a VM [%s] due to insufficient capacity.", vmUuid), e).add(VirtualMachine.class, vmUuid);
|
||||
} catch (final ResourceUnavailableException e) {
|
||||
if (e.getScope() != null && e.getScope().equals(VirtualRouter.class)){
|
||||
if (e.getScope() != null && e.getScope().equals(VirtualRouter.class)) {
|
||||
Account callingAccount = CallContext.current().getCallingAccount();
|
||||
String errorSuffix = (callingAccount != null && callingAccount.getType() == Account.Type.ADMIN) ?
|
||||
String.format("Failure: %s", e.getMessage()) :
|
||||
|
|
@ -1375,6 +1388,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
|
||||
final HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType());
|
||||
|
||||
Throwable lastKnownError = null;
|
||||
boolean canRetry = true;
|
||||
ExcludeList avoids = null;
|
||||
try {
|
||||
|
|
@ -1398,7 +1412,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
|
||||
int retry = StartRetry.value();
|
||||
while (retry-- != 0) {
|
||||
logger.debug("Instance start attempt #{}", (StartRetry.value() - retry));
|
||||
int attemptNumber = StartRetry.value() - retry;
|
||||
logger.debug("Instance start attempt #{}", attemptNumber);
|
||||
|
||||
if (reuseVolume) {
|
||||
final List<VolumeVO> vols = _volsDao.findReadyRootVolumesByInstance(vm.getId());
|
||||
|
|
@ -1464,8 +1479,13 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
reuseVolume = false;
|
||||
continue;
|
||||
}
|
||||
throw new InsufficientServerCapacityException("Unable to create a deployment for " + vmProfile, DataCenter.class, plan.getDataCenterId(),
|
||||
areAffinityGroupsAssociated(vmProfile));
|
||||
String message = String.format("Unable to create a deployment for %s after %s attempts", vmProfile, attemptNumber);
|
||||
if (canExposeError(account) && lastKnownError != null) {
|
||||
message += String.format(" Last known error: %s", lastKnownError.getMessage());
|
||||
throw new CloudRuntimeException(message, lastKnownError);
|
||||
} else {
|
||||
throw new InsufficientServerCapacityException(message, DataCenter.class, plan.getDataCenterId(), areAffinityGroupsAssociated(vmProfile));
|
||||
}
|
||||
}
|
||||
|
||||
avoids.addHost(dest.getHost().getId());
|
||||
|
|
@ -1633,11 +1653,15 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
throw new ExecutionException("Unable to start VM:" + vm.getUuid() + " due to error in finalizeStart, not retrying");
|
||||
}
|
||||
}
|
||||
logger.info("Unable to start VM on {} due to {}", dest.getHost(), (startAnswer == null ? " no start answer" : startAnswer.getDetails()));
|
||||
String msg = String.format("Unable to start VM on %s due to %s", dest.getHost(), startAnswer == null ? "no start command answer" : startAnswer.getDetails());
|
||||
lastKnownError = new ExecutionException(msg);
|
||||
|
||||
if (startAnswer != null && startAnswer.getContextParam("stopRetry") != null) {
|
||||
logger.error(msg, lastKnownError);
|
||||
break;
|
||||
}
|
||||
|
||||
logger.debug(msg, lastKnownError);
|
||||
} catch (OperationTimedoutException e) {
|
||||
logger.debug("Unable to send the start command to host {} failed to start VM: {}", dest.getHost(), vm);
|
||||
if (e.isActive()) {
|
||||
|
|
@ -1647,6 +1671,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
throw new AgentUnavailableException("Unable to start " + vm.getHostName(), destHostId, e);
|
||||
} catch (final ResourceUnavailableException e) {
|
||||
logger.warn("Unable to contact resource.", e);
|
||||
lastKnownError = e;
|
||||
if (!avoids.add(e)) {
|
||||
if (e.getScope() == Volume.class || e.getScope() == Nic.class) {
|
||||
throw e;
|
||||
|
|
@ -1703,10 +1728,22 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
}
|
||||
|
||||
if (startedVm == null) {
|
||||
throw new CloudRuntimeException("Unable to start Instance '" + vm.getHostName() + "' (" + vm.getUuid() + "), see management server log for details");
|
||||
String messageTmpl = "Unable to start Instance '%s' (%s)%s";
|
||||
String details;
|
||||
if (canExposeError(account) && lastKnownError != null) {
|
||||
details = ": " + lastKnownError.getMessage();
|
||||
} else {
|
||||
details = ", see management server log for details";
|
||||
}
|
||||
String message = String.format(messageTmpl, vm.getHostName(), vm.getUuid(), details);
|
||||
throw new CloudRuntimeException(message, lastKnownError);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canExposeError(Account account) {
|
||||
return (account != null && account.getType() == Account.Type.ADMIN) || Boolean.TRUE.equals(EXPOSE_ERRORS_TO_USER.value());
|
||||
}
|
||||
|
||||
protected void updateStartCommandWithExternalDetails(Host host, VirtualMachineTO vmTO, StartCommand command) {
|
||||
if (!HypervisorType.External.equals(host.getHypervisorType())) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -262,7 +262,7 @@ public class DomainDaoImpl extends GenericDaoBase<DomainVO, Long> implements Dom
|
|||
SearchCriteria<DomainVO> sc = DomainPairSearch.create();
|
||||
sc.setParameters("id", parentId, childId);
|
||||
|
||||
List<DomainVO> domainPair = listBy(sc);
|
||||
List<DomainVO> domainPair = listIncludingRemovedBy(sc);
|
||||
|
||||
if ((domainPair != null) && (domainPair.size() == 2)) {
|
||||
DomainVO d1 = domainPair.get(0);
|
||||
|
|
|
|||
|
|
@ -45,4 +45,9 @@ public interface HostTagsDao extends GenericDao<HostTagVO, Long> {
|
|||
HostTagResponse newHostTagResponse(HostTagVO hostTag);
|
||||
|
||||
List<HostTagVO> searchByIds(Long... hostTagIds);
|
||||
|
||||
/**
|
||||
* List all host tags defined on hosts within a cluster
|
||||
*/
|
||||
List<String> listByClusterId(Long clusterId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import org.apache.cloudstack.api.response.HostTagResponse;
|
|||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.Configurable;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
|
@ -43,9 +44,12 @@ public class HostTagsDaoImpl extends GenericDaoBase<HostTagVO, Long> implements
|
|||
private final SearchBuilder<HostTagVO> stSearch;
|
||||
private final SearchBuilder<HostTagVO> tagIdsearch;
|
||||
private final SearchBuilder<HostTagVO> ImplicitTagsSearch;
|
||||
private final GenericSearchBuilder<HostTagVO, String> tagSearch;
|
||||
|
||||
@Inject
|
||||
private ConfigurationDao _configDao;
|
||||
@Inject
|
||||
private HostDao hostDao;
|
||||
|
||||
public HostTagsDaoImpl() {
|
||||
HostSearch = createSearchBuilder();
|
||||
|
|
@ -72,6 +76,11 @@ public class HostTagsDaoImpl extends GenericDaoBase<HostTagVO, Long> implements
|
|||
ImplicitTagsSearch.and("hostId", ImplicitTagsSearch.entity().getHostId(), SearchCriteria.Op.EQ);
|
||||
ImplicitTagsSearch.and("isImplicit", ImplicitTagsSearch.entity().getIsImplicit(), SearchCriteria.Op.EQ);
|
||||
ImplicitTagsSearch.done();
|
||||
|
||||
tagSearch = createSearchBuilder(String.class);
|
||||
tagSearch.selectFields(tagSearch.entity().getTag());
|
||||
tagSearch.and("hostIdIN", tagSearch.entity().getHostId(), SearchCriteria.Op.IN);
|
||||
tagSearch.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -235,4 +244,15 @@ public class HostTagsDaoImpl extends GenericDaoBase<HostTagVO, Long> implements
|
|||
|
||||
return tagList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> listByClusterId(Long clusterId) {
|
||||
List<Long> hostIds = hostDao.listIdsByClusterId(clusterId);
|
||||
if (CollectionUtils.isEmpty(hostIds)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
SearchCriteria<String> sc = tagSearch.create();
|
||||
sc.setParameters("hostIdIN", hostIds.toArray());
|
||||
return customSearch(sc, null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,6 +193,7 @@ public class NetworkDaoImpl extends GenericDaoBase<NetworkVO, Long>implements Ne
|
|||
PersistentNetworkSearch.and("id", PersistentNetworkSearch.entity().getId(), Op.NEQ);
|
||||
PersistentNetworkSearch.and("guestType", PersistentNetworkSearch.entity().getGuestType(), Op.IN);
|
||||
PersistentNetworkSearch.and("broadcastUri", PersistentNetworkSearch.entity().getBroadcastUri(), Op.EQ);
|
||||
PersistentNetworkSearch.and("dc", PersistentNetworkSearch.entity().getDataCenterId(), Op.EQ);
|
||||
PersistentNetworkSearch.and("removed", PersistentNetworkSearch.entity().getRemoved(), Op.NULL);
|
||||
final SearchBuilder<NetworkOfferingVO> persistentNtwkOffJoin = _ntwkOffDao.createSearchBuilder();
|
||||
persistentNtwkOffJoin.and("persistent", persistentNtwkOffJoin.entity().isPersistent(), Op.EQ);
|
||||
|
|
|
|||
|
|
@ -170,6 +170,7 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
|
|||
CountSnapshotsByAccount.select(null, Func.COUNT, null);
|
||||
CountSnapshotsByAccount.and("account", CountSnapshotsByAccount.entity().getAccountId(), SearchCriteria.Op.EQ);
|
||||
CountSnapshotsByAccount.and("status", CountSnapshotsByAccount.entity().getState(), SearchCriteria.Op.NIN);
|
||||
CountSnapshotsByAccount.and("snapshotTypeNEQ", CountSnapshotsByAccount.entity().getSnapshotType(), SearchCriteria.Op.NIN);
|
||||
CountSnapshotsByAccount.and("removed", CountSnapshotsByAccount.entity().getRemoved(), SearchCriteria.Op.NULL);
|
||||
CountSnapshotsByAccount.done();
|
||||
|
||||
|
|
@ -220,6 +221,7 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
|
|||
SearchCriteria<Long> sc = CountSnapshotsByAccount.create();
|
||||
sc.setParameters("account", accountId);
|
||||
sc.setParameters("status", State.Error, State.Destroyed);
|
||||
sc.setParameters("snapshotTypeNEQ", Snapshot.Type.GROUP.ordinal());
|
||||
return customSearch(sc, null).get(0);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,12 @@
|
|||
package com.cloud.upgrade.dao;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.cloud.utils.crypt.DBEncryptionUtil;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
|
||||
public class Upgrade42210to42300 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate {
|
||||
|
|
@ -42,4 +47,46 @@ public class Upgrade42210to42300 extends DbUpgradeAbstractImpl implements DbUpgr
|
|||
|
||||
return new InputStream[] {script};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performDataMigration(Connection conn) {
|
||||
unhideJsInterpretationEnabled(conn);
|
||||
}
|
||||
|
||||
protected void unhideJsInterpretationEnabled(Connection conn) {
|
||||
String value = getJsInterpretationEnabled(conn);
|
||||
if (value != null) {
|
||||
updateJsInterpretationEnabledFields(conn, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected String getJsInterpretationEnabled(Connection conn) {
|
||||
String query = "SELECT value FROM cloud.configuration WHERE name = 'js.interpretation.enabled' AND category = 'Hidden';";
|
||||
|
||||
try (PreparedStatement pstmt = conn.prepareStatement(query)) {
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
if (rs.next()) {
|
||||
return rs.getString("value");
|
||||
}
|
||||
logger.debug("Unable to retrieve value of hidden configuration 'js.interpretation.enabled'. The configuration may already be unhidden.");
|
||||
return null;
|
||||
} catch (SQLException e) {
|
||||
throw new CloudRuntimeException("Error while retrieving value of hidden configuration 'js.interpretation.enabled'.", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateJsInterpretationEnabledFields(Connection conn, String encryptedValue) {
|
||||
String query = "UPDATE cloud.configuration SET value = ?, category = 'System', component = 'JsInterpreter', is_dynamic = 1 WHERE name = 'js.interpretation.enabled';";
|
||||
|
||||
try (PreparedStatement pstmt = conn.prepareStatement(query)) {
|
||||
String decryptedValue = DBEncryptionUtil.decrypt(encryptedValue);
|
||||
logger.info("Updating setting 'js.interpretation.enabled' to decrypted value [{}], category 'System', component 'JsInterpreter', and is_dynamic '1'.", decryptedValue);
|
||||
pstmt.setString(1, decryptedValue);
|
||||
pstmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
throw new CloudRuntimeException("Error while unhiding configuration 'js.interpretation.enabled'.", e);
|
||||
} catch (CloudRuntimeException e) {
|
||||
logger.warn("Error while decrypting configuration 'js.interpretation.enabled'. The configuration may already be decrypted.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ StateDao<ObjectInDataStoreStateMachine.State, ObjectInDataStoreStateMachine.Even
|
|||
|
||||
SnapshotDataStoreVO findBySnapshotIdAndDataStoreRoleAndState(long snapshotId, DataStoreRole role, ObjectInDataStoreStateMachine.State state);
|
||||
|
||||
List<SnapshotDataStoreVO> listBySnapshotIdAndDataStoreRoleAndStateIn(long snapshotId, DataStoreRole role, ObjectInDataStoreStateMachine.State... state);
|
||||
|
||||
List<SnapshotDataStoreVO> listReadyByVolumeIdAndCheckpointPathNotNull(long volumeId);
|
||||
|
||||
SnapshotDataStoreVO findOneBySnapshotId(long snapshotId, long zoneId);
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
|
|||
protected SearchBuilder<SnapshotDataStoreVO> searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq;
|
||||
private SearchBuilder<SnapshotDataStoreVO> stateSearch;
|
||||
private SearchBuilder<SnapshotDataStoreVO> idStateNinSearch;
|
||||
private SearchBuilder<SnapshotDataStoreVO> idEqRoleEqStateInSearch;
|
||||
protected SearchBuilder<SnapshotVO> snapshotVOSearch;
|
||||
private SearchBuilder<SnapshotDataStoreVO> snapshotCreatedSearch;
|
||||
private SearchBuilder<SnapshotDataStoreVO> dataStoreAndInstallPathSearch;
|
||||
|
|
@ -151,6 +152,10 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
|
|||
idStateNinSearch.and(STATE, idStateNinSearch.entity().getState(), SearchCriteria.Op.NOTIN);
|
||||
idStateNinSearch.done();
|
||||
|
||||
idEqRoleEqStateInSearch = createSearchBuilder();
|
||||
idEqRoleEqStateInSearch.and(SNAPSHOT_ID, idEqRoleEqStateInSearch.entity().getSnapshotId(), SearchCriteria.Op.EQ);
|
||||
idEqRoleEqStateInSearch.and(STORE_ROLE, idEqRoleEqStateInSearch.entity().getRole(), SearchCriteria.Op.EQ);
|
||||
idEqRoleEqStateInSearch.and(STATE, idEqRoleEqStateInSearch.entity().getState(), SearchCriteria.Op.IN);
|
||||
|
||||
snapshotVOSearch = snapshotDao.createSearchBuilder();
|
||||
snapshotVOSearch.and(VOLUME_ID, snapshotVOSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
|
||||
|
|
@ -388,6 +393,15 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
|
|||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SnapshotDataStoreVO> listBySnapshotIdAndDataStoreRoleAndStateIn(long snapshotId, DataStoreRole role, State... state) {
|
||||
SearchCriteria<SnapshotDataStoreVO> sc = idEqRoleEqStateInSearch.create();
|
||||
sc.setParameters(SNAPSHOT_ID, snapshotId);
|
||||
sc.setParameters(STORE_ROLE, role);
|
||||
sc.setParameters(STATE, (Object[])state);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnapshotDataStoreVO findOneBySnapshotId(long snapshotId, long zoneId) {
|
||||
try (TransactionLegacy transactionLegacy = TransactionLegacy.currentTxn()) {
|
||||
|
|
|
|||
|
|
@ -687,22 +687,23 @@ CREATE TABLE IF NOT EXISTS `cloud`.`backup_details` (
|
|||
UPDATE `cloud`.`backups` b
|
||||
INNER JOIN `cloud`.`vm_instance` vm ON b.vm_id = vm.id
|
||||
SET b.backed_volumes = (
|
||||
SELECT CONCAT("[",
|
||||
GROUP_CONCAT(
|
||||
CONCAT(
|
||||
"{\"uuid\":\"", v.uuid, "\",",
|
||||
"\"type\":\"", v.volume_type, "\",",
|
||||
"\"size\":", v.`size`, ",",
|
||||
"\"path\":\"", IFNULL(v.path, 'null'), "\",",
|
||||
"\"deviceId\":", IFNULL(v.device_id, 'null'), ",",
|
||||
"\"diskOfferingId\":\"", doff.uuid, "\",",
|
||||
"\"minIops\":", IFNULL(v.min_iops, 'null'), ",",
|
||||
"\"maxIops\":", IFNULL(v.max_iops, 'null'),
|
||||
"}"
|
||||
)
|
||||
SEPARATOR ","
|
||||
SELECT COALESCE(
|
||||
CAST(
|
||||
JSON_ARRAYAGG(
|
||||
JSON_OBJECT(
|
||||
'uuid', v.uuid,
|
||||
'type', v.volume_type,
|
||||
'size', v.size,
|
||||
'path', v.path,
|
||||
'deviceId', v.device_id,
|
||||
'diskOfferingId', doff.uuid,
|
||||
'minIops', v.min_iops,
|
||||
'maxIops', v.max_iops
|
||||
)
|
||||
) AS CHAR
|
||||
),
|
||||
"]")
|
||||
'[]'
|
||||
)
|
||||
FROM `cloud`.`volumes` v
|
||||
LEFT JOIN `cloud`.`disk_offering` doff ON v.disk_offering_id = doff.id
|
||||
WHERE v.instance_id = vm.id
|
||||
|
|
@ -711,22 +712,23 @@ SET b.backed_volumes = (
|
|||
-- Add diskOfferingId, deviceId, minIops and maxIops to backup_volumes in vm_instance table
|
||||
UPDATE `cloud`.`vm_instance` vm
|
||||
SET vm.backup_volumes = (
|
||||
SELECT CONCAT("[",
|
||||
GROUP_CONCAT(
|
||||
CONCAT(
|
||||
"{\"uuid\":\"", v.uuid, "\",",
|
||||
"\"type\":\"", v.volume_type, "\",",
|
||||
"\"size\":", v.`size`, ",",
|
||||
"\"path\":\"", IFNULL(v.path, 'null'), "\",",
|
||||
"\"deviceId\":", IFNULL(v.device_id, 'null'), ",",
|
||||
"\"diskOfferingId\":\"", doff.uuid, "\",",
|
||||
"\"minIops\":", IFNULL(v.min_iops, 'null'), ",",
|
||||
"\"maxIops\":", IFNULL(v.max_iops, 'null'),
|
||||
"}"
|
||||
)
|
||||
SEPARATOR ","
|
||||
SELECT COALESCE(
|
||||
CAST(
|
||||
JSON_ARRAYAGG(
|
||||
JSON_OBJECT(
|
||||
'uuid', v.uuid,
|
||||
'type', v.volume_type,
|
||||
'size', v.size,
|
||||
'path', v.path,
|
||||
'deviceId', v.device_id,
|
||||
'diskOfferingId', doff.uuid,
|
||||
'minIops', v.min_iops,
|
||||
'maxIops', v.max_iops
|
||||
)
|
||||
) AS CHAR
|
||||
),
|
||||
"]")
|
||||
'[]'
|
||||
)
|
||||
FROM `cloud`.`volumes` v
|
||||
LEFT JOIN `cloud`.`disk_offering` doff ON v.disk_offering_id = doff.id
|
||||
WHERE v.instance_id = vm.id
|
||||
|
|
|
|||
|
|
@ -18,5 +18,3 @@
|
|||
--;
|
||||
-- Schema upgrade cleanup from 4.22.0.0 to 4.22.1.0
|
||||
--;
|
||||
|
||||
DROP VIEW IF EXISTS `cloud`.`account_netstats_view`;
|
||||
|
|
|
|||
|
|
@ -35,3 +35,6 @@ UPDATE `cloud`.`alert` SET type = 34 WHERE name = 'ALERT.VR.PRIVATE.IFACE.MTU';
|
|||
UPDATE `cloud`.`configuration` SET description = 'True if the management server will restart the agent service via SSH into the KVM hosts after or during maintenance operations', is_dynamic = 1 WHERE name = 'kvm.ssh.to.agent';
|
||||
|
||||
UPDATE `cloud`.`vm_template` SET guest_os_id = 99 WHERE name = 'kvm-default-vm-import-dummy-template';
|
||||
|
||||
-- Update existing vm_template records with NULL type to "USER"
|
||||
UPDATE `cloud`.`vm_template` SET `type` = 'USER' WHERE `type` IS NULL;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
-- Licensed to the Apache Software Foundation (ASF) under one
|
||||
-- or more contributor license agreements. See the NOTICE file
|
||||
-- distributed with this work for additional information
|
||||
-- regarding copyright ownership. The ASF licenses this file
|
||||
-- to you under the Apache License, Version 2.0 (the
|
||||
-- "License"); you may not use this file except in compliance
|
||||
-- with the License. You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing,
|
||||
-- software distributed under the License is distributed on an
|
||||
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
-- KIND, either express or implied. See the License for the
|
||||
-- specific language governing permissions and limitations
|
||||
-- under the License.
|
||||
|
||||
-- cloud.account_netstats_view source
|
||||
|
||||
|
||||
DROP VIEW IF EXISTS `cloud`.`account_netstats_view`;
|
||||
|
||||
CREATE VIEW `cloud`.`account_netstats_view` AS
|
||||
select
|
||||
`user_statistics`.`account_id` AS `account_id`,
|
||||
(sum(`user_statistics`.`net_bytes_received`) + sum(`user_statistics`.`current_bytes_received`)) AS `bytesReceived`,
|
||||
(sum(`user_statistics`.`net_bytes_sent`) + sum(`user_statistics`.`current_bytes_sent`)) AS `bytesSent`
|
||||
from
|
||||
`user_statistics`
|
||||
group by
|
||||
`user_statistics`.`account_id`;
|
||||
|
|
@ -39,8 +39,8 @@ select
|
|||
`data_center`.`id` AS `data_center_id`,
|
||||
`data_center`.`uuid` AS `data_center_uuid`,
|
||||
`data_center`.`name` AS `data_center_name`,
|
||||
`account_netstats`.`bytesReceived` AS `bytesReceived`,
|
||||
`account_netstats`.`bytesSent` AS `bytesSent`,
|
||||
`account_netstats_view`.`bytesReceived` AS `bytesReceived`,
|
||||
`account_netstats_view`.`bytesSent` AS `bytesSent`,
|
||||
`vmlimit`.`max` AS `vmLimit`,
|
||||
`vmcount`.`count` AS `vmTotal`,
|
||||
`runningvm`.`vmcount` AS `runningVms`,
|
||||
|
|
@ -89,15 +89,8 @@ from
|
|||
`cloud`.`domain` ON account.domain_id = domain.id
|
||||
left join
|
||||
`cloud`.`data_center` ON account.default_zone_id = data_center.id
|
||||
left join lateral (
|
||||
select
|
||||
coalesce(sum(`user_statistics`.`net_bytes_received` + `user_statistics`.`current_bytes_received`), 0) AS `bytesReceived`,
|
||||
coalesce(sum(`user_statistics`.`net_bytes_sent` + `user_statistics`.`current_bytes_sent`), 0) AS `bytesSent`
|
||||
from
|
||||
`cloud`.`user_statistics`
|
||||
where
|
||||
`user_statistics`.`account_id` = `account`.`id`
|
||||
) AS `account_netstats` ON TRUE
|
||||
left join
|
||||
`cloud`.`account_netstats_view` ON account.id = account_netstats_view.account_id
|
||||
left join
|
||||
`cloud`.`resource_limit` vmlimit ON account.id = vmlimit.account_id
|
||||
and vmlimit.type = 'user_vm' and vmlimit.tag IS NULL
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
-- Licensed to the Apache Software Foundation (ASF) under one
|
||||
-- or more contributor license agreements. See the NOTICE file
|
||||
-- distributed with this work for additional information
|
||||
-- regarding copyright ownership. The ASF licenses this file
|
||||
-- to you under the Apache License, Version 2.0 (the
|
||||
-- "License"); you may not use this file except in compliance
|
||||
-- with the License. You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing,
|
||||
-- software distributed under the License is distributed on an
|
||||
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
-- KIND, either express or implied. See the License for the
|
||||
-- specific language governing permissions and limitations
|
||||
-- under the License.
|
||||
|
||||
-- cloud_usage.quota_summary_view source
|
||||
|
||||
-- Create view for quota summary
|
||||
DROP VIEW IF EXISTS `cloud_usage`.`quota_summary_view`;
|
||||
CREATE VIEW `cloud_usage`.`quota_summary_view` AS
|
||||
SELECT
|
||||
cloud_usage.quota_account.account_id AS account_id,
|
||||
cloud_usage.quota_account.quota_balance AS quota_balance,
|
||||
cloud_usage.quota_account.quota_balance_date AS quota_balance_date,
|
||||
cloud_usage.quota_account.quota_enforce AS quota_enforce,
|
||||
cloud_usage.quota_account.quota_min_balance AS quota_min_balance,
|
||||
cloud_usage.quota_account.quota_alert_date AS quota_alert_date,
|
||||
cloud_usage.quota_account.quota_alert_type AS quota_alert_type,
|
||||
cloud_usage.quota_account.last_statement_date AS last_statement_date,
|
||||
cloud.account.uuid AS account_uuid,
|
||||
cloud.account.account_name AS account_name,
|
||||
cloud.account.state AS account_state,
|
||||
cloud.account.removed AS account_removed,
|
||||
cloud.domain.id AS domain_id,
|
||||
cloud.domain.uuid AS domain_uuid,
|
||||
cloud.domain.name AS domain_name,
|
||||
cloud.domain.path AS domain_path,
|
||||
cloud.domain.removed AS domain_removed,
|
||||
cloud.projects.uuid AS project_uuid,
|
||||
cloud.projects.name AS project_name,
|
||||
cloud.projects.removed AS project_removed
|
||||
FROM
|
||||
cloud_usage.quota_account
|
||||
INNER JOIN cloud.account ON (cloud.account.id = cloud_usage.quota_account.account_id)
|
||||
INNER JOIN cloud.domain ON (cloud.domain.id = cloud.account.domain_id)
|
||||
LEFT JOIN cloud.projects ON (cloud.account.type = 5 AND cloud.account.id = cloud.projects.project_account_id);
|
||||
|
|
@ -121,7 +121,7 @@ public class DefaultSnapshotStrategy extends SnapshotStrategyBase {
|
|||
private final List<Snapshot.State> snapshotStatesAbleToDeleteSnapshot = Arrays.asList(Snapshot.State.Destroying, Snapshot.State.Destroyed, Snapshot.State.Error, Snapshot.State.Hidden);
|
||||
|
||||
public SnapshotDataStoreVO getSnapshotImageStoreRef(long snapshotId, long zoneId) {
|
||||
List<SnapshotDataStoreVO> snaps = snapshotStoreDao.listReadyBySnapshot(snapshotId, DataStoreRole.Image);
|
||||
List<SnapshotDataStoreVO> snaps = snapshotStoreDao.listBySnapshotIdAndDataStoreRoleAndStateIn(snapshotId, DataStoreRole.Image, State.Ready, State.Hidden);
|
||||
for (SnapshotDataStoreVO ref : snaps) {
|
||||
if (zoneId == dataStoreMgr.getStoreZoneId(ref.getDataStoreId(), ref.getRole())) {
|
||||
return ref;
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
|||
import org.apache.cloudstack.storage.datastore.util.ScaleIOUtil;
|
||||
import org.apache.cloudstack.storage.to.VolumeObjectTO;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.cloudstack.storage.datastore.api.Volume;
|
||||
|
||||
import com.cloud.agent.api.VMSnapshotTO;
|
||||
import com.cloud.alert.AlertManager;
|
||||
|
|
@ -200,11 +201,35 @@ public class ScaleIOVMSnapshotStrategy extends ManagerBase implements VMSnapshot
|
|||
if (volumeIds != null && !volumeIds.isEmpty()) {
|
||||
List<VMSnapshotDetailsVO> vmSnapshotDetails = new ArrayList<VMSnapshotDetailsVO>();
|
||||
vmSnapshotDetails.add(new VMSnapshotDetailsVO(vmSnapshot.getId(), "SnapshotGroupId", snapshotGroupId, false));
|
||||
Map<String, String> snapshotNameToSrcPathMap = new HashMap<>();
|
||||
for (Map.Entry<String, String> entry : srcVolumeDestSnapshotMap.entrySet()) {
|
||||
snapshotNameToSrcPathMap.put(entry.getValue(), entry.getKey());
|
||||
}
|
||||
|
||||
for (int index = 0; index < volumeIds.size(); index++) {
|
||||
String volumeSnapshotName = srcVolumeDestSnapshotMap.get(ScaleIOUtil.getVolumePath(volumeTOs.get(index).getPath()));
|
||||
String pathWithScaleIOVolumeName = ScaleIOUtil.updatedPathWithVolumeName(volumeIds.get(index), volumeSnapshotName);
|
||||
vmSnapshotDetails.add(new VMSnapshotDetailsVO(vmSnapshot.getId(), "Vol_" + volumeTOs.get(index).getId() + "_Snapshot", pathWithScaleIOVolumeName, false));
|
||||
for (String snapshotVolumeId : volumeIds) {
|
||||
// Use getVolume() to fetch snapshot volume details and get its name
|
||||
Volume snapshotVolume = client.getVolume(snapshotVolumeId);
|
||||
if (snapshotVolume == null) {
|
||||
throw new CloudRuntimeException("Cannot find snapshot volume with id: " + snapshotVolumeId);
|
||||
}
|
||||
String snapshotName = snapshotVolume.getName();
|
||||
|
||||
// Match back to source volume path
|
||||
String srcVolumePath = snapshotNameToSrcPathMap.get(snapshotName);
|
||||
if (srcVolumePath == null) {
|
||||
throw new CloudRuntimeException("Cannot match snapshot " + snapshotName + " to a source volume");
|
||||
}
|
||||
|
||||
// Find the matching VolumeObjectTO by path
|
||||
VolumeObjectTO matchedVolume = volumeTOs.stream()
|
||||
.filter(v -> ScaleIOUtil.getVolumePath(v.getPath()).equals(srcVolumePath))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new CloudRuntimeException("Cannot find source volume for path: " + srcVolumePath));
|
||||
|
||||
String pathWithScaleIOVolumeName = ScaleIOUtil.updatedPathWithVolumeName(snapshotVolumeId, snapshotName);
|
||||
vmSnapshotDetails.add(new VMSnapshotDetailsVO(vmSnapshot.getId(),
|
||||
"Vol_" + matchedVolume.getId() + "_Snapshot",
|
||||
pathWithScaleIOVolumeName, false));
|
||||
}
|
||||
|
||||
vmSnapshotDetailsDao.saveDetails(vmSnapshotDetails);
|
||||
|
|
|
|||
|
|
@ -258,11 +258,6 @@ public class DefaultSnapshotStrategyTest {
|
|||
|
||||
@Test
|
||||
public void testGetSnapshotImageStoreRefNull() {
|
||||
SnapshotDataStoreVO ref1 = Mockito.mock(SnapshotDataStoreVO.class);
|
||||
Mockito.when(ref1.getDataStoreId()).thenReturn(1L);
|
||||
Mockito.when(ref1.getRole()).thenReturn(DataStoreRole.Image);
|
||||
Mockito.when(snapshotDataStoreDao.listReadyBySnapshot(Mockito.anyLong(), Mockito.any(DataStoreRole.class))).thenReturn(List.of(ref1));
|
||||
Mockito.when(dataStoreManager.getStoreZoneId(1L, DataStoreRole.Image)).thenReturn(2L);
|
||||
Assert.assertNull(defaultSnapshotStrategySpy.getSnapshotImageStoreRef(1L, 1L));
|
||||
}
|
||||
|
||||
|
|
@ -271,7 +266,7 @@ public class DefaultSnapshotStrategyTest {
|
|||
SnapshotDataStoreVO ref1 = Mockito.mock(SnapshotDataStoreVO.class);
|
||||
Mockito.when(ref1.getDataStoreId()).thenReturn(1L);
|
||||
Mockito.when(ref1.getRole()).thenReturn(DataStoreRole.Image);
|
||||
Mockito.when(snapshotDataStoreDao.listReadyBySnapshot(Mockito.anyLong(), Mockito.any(DataStoreRole.class))).thenReturn(List.of(ref1));
|
||||
Mockito.when(snapshotDataStoreDao.listBySnapshotIdAndDataStoreRoleAndStateIn(Mockito.anyLong(), Mockito.any(DataStoreRole.class), Mockito.any(), Mockito.any())).thenReturn(List.of(ref1));
|
||||
Mockito.when(dataStoreManager.getStoreZoneId(1L, DataStoreRole.Image)).thenReturn(1L);
|
||||
Assert.assertNotNull(defaultSnapshotStrategySpy.getSnapshotImageStoreRef(1L, 1L));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
package org.apache.cloudstack.quota;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public enum QuotaAccountStateFilter {
|
||||
ALL, ACTIVE, REMOVED;
|
||||
|
||||
public static QuotaAccountStateFilter getValue(String value) {
|
||||
if (StringUtils.isBlank(value)) {
|
||||
return null;
|
||||
}
|
||||
for (QuotaAccountStateFilter state : values()) {
|
||||
if (state.name().equalsIgnoreCase(value)) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.cloudstack.quota.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.quota.QuotaAccountStateFilter;
|
||||
import org.apache.cloudstack.quota.vo.QuotaSummaryVO;
|
||||
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
public interface QuotaSummaryDao extends GenericDao<QuotaSummaryVO, Long> {
|
||||
|
||||
Pair<List<QuotaSummaryVO>, Integer> listQuotaSummariesForAccountAndOrDomain(Long accountId, String accountName, Long domainId, String domainPath,
|
||||
QuotaAccountStateFilter accountStateFilter, Long startIndex, Long pageSize);
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.cloudstack.quota.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.quota.QuotaAccountStateFilter;
|
||||
import org.apache.cloudstack.quota.vo.QuotaSummaryVO;
|
||||
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.Filter;
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import com.cloud.utils.db.Transaction;
|
||||
import com.cloud.utils.db.TransactionCallback;
|
||||
import com.cloud.utils.db.TransactionLegacy;
|
||||
|
||||
public class QuotaSummaryDaoImpl extends GenericDaoBase<QuotaSummaryVO, Long> implements QuotaSummaryDao {
|
||||
|
||||
@Override
|
||||
public Pair<List<QuotaSummaryVO>, Integer> listQuotaSummariesForAccountAndOrDomain(Long accountId, String accountName, Long domainId, String domainPath,
|
||||
QuotaAccountStateFilter accountStateFilter, Long startIndex, Long pageSize) {
|
||||
SearchCriteria<QuotaSummaryVO> searchCriteria = createListQuotaSummariesSearchCriteria(accountId, accountName, domainId, domainPath, accountStateFilter);
|
||||
Filter filter = new Filter(QuotaSummaryVO.class, "accountName", true, startIndex, pageSize);
|
||||
|
||||
return Transaction.execute(TransactionLegacy.USAGE_DB, (TransactionCallback<Pair<List<QuotaSummaryVO>, Integer>>) status -> searchAndCount(searchCriteria, filter));
|
||||
}
|
||||
|
||||
protected SearchCriteria<QuotaSummaryVO> createListQuotaSummariesSearchCriteria(Long accountId, String accountName, Long domainId, String domainPath,
|
||||
QuotaAccountStateFilter accountStateFilter) {
|
||||
SearchCriteria<QuotaSummaryVO> searchCriteria = createListQuotaSummariesSearchBuilder(accountStateFilter).create();
|
||||
|
||||
searchCriteria.setParametersIfNotNull("accountId", accountId);
|
||||
searchCriteria.setParametersIfNotNull("domainId", domainId);
|
||||
|
||||
if (accountName != null) {
|
||||
searchCriteria.setParameters("accountName", "%" + accountName + "%");
|
||||
}
|
||||
|
||||
if (domainPath != null) {
|
||||
searchCriteria.setParameters("domainPath", domainPath + "%");
|
||||
}
|
||||
|
||||
return searchCriteria;
|
||||
}
|
||||
|
||||
protected SearchBuilder<QuotaSummaryVO> createListQuotaSummariesSearchBuilder(QuotaAccountStateFilter accountStateFilter) {
|
||||
SearchBuilder<QuotaSummaryVO> searchBuilder = createSearchBuilder();
|
||||
|
||||
searchBuilder.and("accountId", searchBuilder.entity().getAccountId(), SearchCriteria.Op.EQ);
|
||||
searchBuilder.and("accountName", searchBuilder.entity().getAccountName(), SearchCriteria.Op.LIKE);
|
||||
searchBuilder.and("domainId", searchBuilder.entity().getDomainId(), SearchCriteria.Op.EQ);
|
||||
searchBuilder.and("domainPath", searchBuilder.entity().getDomainPath(), SearchCriteria.Op.LIKE);
|
||||
|
||||
if (QuotaAccountStateFilter.REMOVED.equals(accountStateFilter)) {
|
||||
searchBuilder.and("accountRemoved", searchBuilder.entity().getAccountRemoved(), SearchCriteria.Op.NNULL);
|
||||
} else if (QuotaAccountStateFilter.ACTIVE.equals(accountStateFilter)) {
|
||||
searchBuilder.and("accountRemoved", searchBuilder.entity().getAccountRemoved(), SearchCriteria.Op.NULL);
|
||||
}
|
||||
|
||||
return searchBuilder;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.cloudstack.quota.vo;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import com.cloud.user.Account;
|
||||
|
||||
@Entity
|
||||
@Table(name = "quota_summary_view")
|
||||
public class QuotaSummaryVO {
|
||||
|
||||
@Id
|
||||
@Column(name = "account_id")
|
||||
private Long accountId = null;
|
||||
|
||||
@Column(name = "quota_enforce")
|
||||
private Integer quotaEnforce = 0;
|
||||
|
||||
@Column(name = "quota_balance")
|
||||
private BigDecimal quotaBalance;
|
||||
|
||||
@Column(name = "quota_balance_date")
|
||||
@Temporal(value = TemporalType.TIMESTAMP)
|
||||
private Date quotaBalanceDate = null;
|
||||
|
||||
@Column(name = "quota_min_balance")
|
||||
private BigDecimal quotaMinBalance;
|
||||
|
||||
@Column(name = "quota_alert_type")
|
||||
private Integer quotaAlertType = null;
|
||||
|
||||
@Column(name = "quota_alert_date")
|
||||
@Temporal(value = TemporalType.TIMESTAMP)
|
||||
private Date quotaAlertDate = null;
|
||||
|
||||
@Column(name = "last_statement_date")
|
||||
@Temporal(value = TemporalType.TIMESTAMP)
|
||||
private Date lastStatementDate = null;
|
||||
|
||||
@Column(name = "account_uuid")
|
||||
private String accountUuid;
|
||||
|
||||
@Column(name = "account_name")
|
||||
private String accountName;
|
||||
|
||||
@Column(name = "account_state")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Account.State accountState;
|
||||
|
||||
@Column(name = "account_removed")
|
||||
private Date accountRemoved;
|
||||
|
||||
@Column(name = "domain_id")
|
||||
private Long domainId;
|
||||
|
||||
@Column(name = "domain_uuid")
|
||||
private String domainUuid;
|
||||
|
||||
@Column(name = "domain_name")
|
||||
private String domainName;
|
||||
|
||||
@Column(name = "domain_path")
|
||||
private String domainPath;
|
||||
|
||||
@Column(name = "domain_removed")
|
||||
private Date domainRemoved;
|
||||
|
||||
@Column(name = "project_uuid")
|
||||
private String projectUuid;
|
||||
|
||||
@Column(name = "project_name")
|
||||
private String projectName;
|
||||
|
||||
@Column(name = "project_removed")
|
||||
private Date projectRemoved;
|
||||
|
||||
public Long getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public BigDecimal getQuotaBalance() {
|
||||
return quotaBalance;
|
||||
}
|
||||
|
||||
public String getAccountUuid() {
|
||||
return accountUuid;
|
||||
}
|
||||
|
||||
public String getAccountName() {
|
||||
return accountName;
|
||||
}
|
||||
|
||||
public Date getAccountRemoved() {
|
||||
return accountRemoved;
|
||||
}
|
||||
|
||||
public Account.State getAccountState() {
|
||||
return accountState;
|
||||
}
|
||||
|
||||
public Long getDomainId() {
|
||||
return domainId;
|
||||
}
|
||||
|
||||
public String getDomainUuid() {
|
||||
return domainUuid;
|
||||
}
|
||||
|
||||
public String getDomainPath() {
|
||||
return domainPath;
|
||||
}
|
||||
|
||||
public Date getDomainRemoved() {
|
||||
return domainRemoved;
|
||||
}
|
||||
|
||||
public String getProjectUuid() {
|
||||
return projectUuid;
|
||||
}
|
||||
|
||||
public String getProjectName() {
|
||||
return projectName;
|
||||
}
|
||||
|
||||
public Date getProjectRemoved() {
|
||||
return projectRemoved;
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,8 @@
|
|||
|
||||
<bean id="presetVariableHelper" class="org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableHelper" />
|
||||
<bean id="QuotaTariffDao" class="org.apache.cloudstack.quota.dao.QuotaTariffDaoImpl" />
|
||||
<bean id="QuotaAccountDao" class="org.apache.cloudstack.quota.dao.QuotaAccountDaoImpl" />
|
||||
<bean id="QuotaSummaryDao" class="org.apache.cloudstack.quota.dao.QuotaSummaryDaoImpl" />
|
||||
<bean id="QuotaAccountDao" class="org.apache.cloudstack.quota.dao.QuotaAccountDaoImpl" />
|
||||
<bean id="QuotaBalanceDao" class="org.apache.cloudstack.quota.dao.QuotaBalanceDaoImpl" />
|
||||
<bean id="QuotaCreditsDao" class="org.apache.cloudstack.quota.dao.QuotaCreditsDaoImpl" />
|
||||
<bean id="QuotaEmailTemplatesDao"
|
||||
|
|
|
|||
|
|
@ -357,7 +357,8 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
|
|||
volumePools.add(dataStore != null ? (PrimaryDataStoreTO)dataStore.getTO() : null);
|
||||
|
||||
String volumePathPrefix = getVolumePathPrefix(storagePool);
|
||||
volumePaths.add(String.format("%s/%s", volumePathPrefix, volume.getPath()));
|
||||
String volumePathSuffix = getVolumePathSuffix(storagePool);
|
||||
volumePaths.add(String.format("%s%s%s", volumePathPrefix, volume.getPath(), volumePathSuffix));
|
||||
}
|
||||
return new Pair<>(volumePools, volumePaths);
|
||||
}
|
||||
|
|
@ -367,14 +368,24 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
|
|||
if (ScopeType.HOST.equals(storagePool.getScope()) ||
|
||||
Storage.StoragePoolType.SharedMountPoint.equals(storagePool.getPoolType()) ||
|
||||
Storage.StoragePoolType.RBD.equals(storagePool.getPoolType())) {
|
||||
volumePathPrefix = storagePool.getPath();
|
||||
volumePathPrefix = storagePool.getPath() + "/";
|
||||
} else if (Storage.StoragePoolType.Linstor.equals(storagePool.getPoolType())) {
|
||||
volumePathPrefix = "/dev/drbd/by-res/cs-";
|
||||
} else {
|
||||
// Should be Storage.StoragePoolType.NetworkFilesystem
|
||||
volumePathPrefix = String.format("/mnt/%s", storagePool.getUuid());
|
||||
volumePathPrefix = String.format("/mnt/%s/", storagePool.getUuid());
|
||||
}
|
||||
return volumePathPrefix;
|
||||
}
|
||||
|
||||
private String getVolumePathSuffix(StoragePoolVO storagePool) {
|
||||
if (Storage.StoragePoolType.Linstor.equals(storagePool.getPoolType())) {
|
||||
return "/0";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<Boolean, String> restoreBackedUpVolume(Backup backup, Backup.VolumeInfo backupVolumeInfo, String hostIp, String dataStoreUuid, Pair<String, VirtualMachine.State> vmNameAndState) {
|
||||
final VolumeVO volume = volumeDao.findByUuid(backupVolumeInfo.getUuid());
|
||||
|
|
@ -419,7 +430,9 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
|
|||
restoreCommand.setBackupRepoType(backupRepository.getType());
|
||||
restoreCommand.setBackupRepoAddress(backupRepository.getAddress());
|
||||
restoreCommand.setVmName(vmNameAndState.first());
|
||||
restoreCommand.setRestoreVolumePaths(Collections.singletonList(String.format("%s/%s", getVolumePathPrefix(pool), volumeUUID)));
|
||||
String restoreVolumePath = String.format("%s%s%s", getVolumePathPrefix(pool), volumeUUID, getVolumePathSuffix(pool));
|
||||
restoreCommand.setRestoreVolumePaths(Collections.singletonList(restoreVolumePath));
|
||||
restoreCommand.setRestoreVolumeSizes(Collections.singletonList(backedUpVolumeSize));
|
||||
DataStore dataStore = dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary);
|
||||
restoreCommand.setRestoreVolumePools(Collections.singletonList(dataStore != null ? (PrimaryDataStoreTO)dataStore.getTO() : null));
|
||||
restoreCommand.setDiskType(backupVolumeInfo.getType().name().toLowerCase(Locale.ROOT));
|
||||
|
|
@ -478,6 +491,9 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
|
|||
} else {
|
||||
host = resourceManager.findOneRandomRunningHostByHypervisor(Hypervisor.HypervisorType.KVM, backup.getZoneId());
|
||||
}
|
||||
if (host == null) {
|
||||
throw new CloudRuntimeException(String.format("Unable to find a running KVM host in zone %d to delete backup %s", backup.getZoneId(), backup.getUuid()));
|
||||
}
|
||||
|
||||
DeleteBackupCommand command = new DeleteBackupCommand(backup.getExternalId(), backupRepository.getType(),
|
||||
backupRepository.getAddress(), backupRepository.getMountOptions());
|
||||
|
|
@ -564,7 +580,14 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
|
|||
@Override
|
||||
public void syncBackupStorageStats(Long zoneId) {
|
||||
final List<BackupRepository> repositories = backupRepositoryDao.listByZoneAndProvider(zoneId, getName());
|
||||
if (CollectionUtils.isEmpty(repositories)) {
|
||||
return;
|
||||
}
|
||||
final Host host = resourceManager.findOneRandomRunningHostByHypervisor(Hypervisor.HypervisorType.KVM, zoneId);
|
||||
if (host == null) {
|
||||
logger.warn("Unable to find a running KVM host in zone {} to sync backup storage stats", zoneId);
|
||||
return;
|
||||
}
|
||||
for (final BackupRepository repository : repositories) {
|
||||
GetBackupStorageStatsCommand command = new GetBackupStorageStatsCommand(repository.getType(), repository.getAddress(), repository.getMountOptions());
|
||||
BackupStorageStatsAnswer answer;
|
||||
|
|
|
|||
|
|
@ -16,61 +16,80 @@
|
|||
//under the License.
|
||||
package org.apache.cloudstack.api.command;
|
||||
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.utils.Pair;
|
||||
|
||||
|
||||
import org.apache.cloudstack.api.ACL;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseListCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.response.AccountResponse;
|
||||
import org.apache.cloudstack.api.response.DomainResponse;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.QuotaResponseBuilder;
|
||||
import org.apache.cloudstack.api.response.QuotaSummaryResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.api.response.ProjectResponse;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.quota.QuotaAccountStateFilter;
|
||||
import org.apache.cloudstack.quota.QuotaService;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@APICommand(name = "quotaSummary", responseObject = QuotaSummaryResponse.class, description = "Lists balance and quota usage for all Accounts", since = "4.7.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
|
||||
httpMethod = "GET")
|
||||
@APICommand(name = "quotaSummary", responseObject = QuotaSummaryResponse.class, description = "Lists Quota balance summary of Accounts and Projects.", since = "4.7.0",
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, httpMethod = "GET")
|
||||
public class QuotaSummaryCmd extends BaseListCmd {
|
||||
|
||||
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, required = false, description = "Optional, Account Id for which statement needs to be generated")
|
||||
private String accountName;
|
||||
|
||||
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, required = false, entityType = DomainResponse.class, description = "Optional, If domain Id is given and the caller is domain admin then the statement is generated for domain.")
|
||||
private Long domainId;
|
||||
|
||||
@Parameter(name = ApiConstants.LIST_ALL, type = CommandType.BOOLEAN, required = false, description = "Optional, to list all Accounts irrespective of the quota activity")
|
||||
private Boolean listAll;
|
||||
@Inject
|
||||
QuotaResponseBuilder quotaResponseBuilder;
|
||||
|
||||
@Inject
|
||||
QuotaResponseBuilder _responseBuilder;
|
||||
QuotaService quotaService;
|
||||
|
||||
public QuotaSummaryCmd() {
|
||||
super();
|
||||
}
|
||||
@ACL
|
||||
@Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "ID of the Account for which balance will be listed. Can not be specified with projectid.", since = "4.23.0")
|
||||
private Long accountId;
|
||||
|
||||
@ACL
|
||||
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, required = false, description = "Name of the Account for which balance will be listed.")
|
||||
private String accountName;
|
||||
|
||||
@ACL
|
||||
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, required = false, entityType = DomainResponse.class, description = "ID of the Domain for which balance will be listed. May be used individually or with accountname.")
|
||||
private Long domainId;
|
||||
|
||||
@Parameter(name = ApiConstants.LIST_ALL, type = CommandType.BOOLEAN, description = "False (default) lists the Quota balance summary for calling Account. True lists balance summary for " +
|
||||
"Accounts which the caller has access. If domain ID is informed, this parameter is considered as true.")
|
||||
private Boolean listAll;
|
||||
|
||||
@Parameter(name = ApiConstants.ACCOUNT_STATE_TO_SHOW, type = CommandType.STRING, description = "Possible values are [ALL, ACTIVE, REMOVED]. ALL will list summaries for " +
|
||||
"active and removed accounts; ACTIVE will list summaries only for active accounts; REMOVED will list summaries only for removed accounts. The default value is ACTIVE.",
|
||||
since = "4.23.0")
|
||||
private String accountStateToShow;
|
||||
|
||||
@ACL
|
||||
@Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "ID of the Project for which balance will be listed. Can not be specified with accountId.", since = "4.23.0")
|
||||
private Long projectId;
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
Pair<List<QuotaSummaryResponse>, Integer> responses;
|
||||
if (caller.getType() == Account.Type.ADMIN) {
|
||||
if (getAccountName() != null && getDomainId() != null)
|
||||
responses = _responseBuilder.createQuotaSummaryResponse(getAccountName(), getDomainId());
|
||||
else
|
||||
responses = _responseBuilder.createQuotaSummaryResponse(isListAll(), getKeyword(), getStartIndex(), getPageSizeVal());
|
||||
} else {
|
||||
responses = _responseBuilder.createQuotaSummaryResponse(caller.getAccountName(), caller.getDomainId());
|
||||
}
|
||||
final ListResponse<QuotaSummaryResponse> response = new ListResponse<QuotaSummaryResponse>();
|
||||
Pair<List<QuotaSummaryResponse>, Integer> responses = quotaResponseBuilder.createQuotaSummaryResponse(this);
|
||||
ListResponse<QuotaSummaryResponse> response = new ListResponse<>();
|
||||
response.setResponses(responses.first(), responses.second());
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
public Long getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public void setAccountId(Long accountId) {
|
||||
this.accountId = accountId;
|
||||
}
|
||||
|
||||
public String getAccountName() {
|
||||
return accountName;
|
||||
}
|
||||
|
|
@ -88,16 +107,31 @@ public class QuotaSummaryCmd extends BaseListCmd {
|
|||
}
|
||||
|
||||
public Boolean isListAll() {
|
||||
return listAll == null ? false: listAll;
|
||||
// If a domain ID was specified, then allow listing all summaries of domain
|
||||
return ObjectUtils.defaultIfNull(listAll, Boolean.FALSE) || domainId != null;
|
||||
}
|
||||
|
||||
public void setListAll(Boolean listAll) {
|
||||
this.listAll = listAll;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return Account.ACCOUNT_ID_SYSTEM;
|
||||
public Long getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public QuotaAccountStateFilter getAccountStateToShow() {
|
||||
QuotaAccountStateFilter state = QuotaAccountStateFilter.getValue(accountStateToShow);
|
||||
if (state != null) {
|
||||
return state;
|
||||
}
|
||||
return QuotaAccountStateFilter.ACTIVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
if (ObjectUtils.allNull(accountId, accountName, projectId)) {
|
||||
return -1;
|
||||
}
|
||||
return _accountService.finalizeAccountId(accountId, accountName, domainId, projectId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
|
|||
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaPresetVariablesListCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaStatementCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaSummaryCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaTariffCreateCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaTariffListCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
|
||||
|
|
@ -52,11 +53,7 @@ public interface QuotaResponseBuilder {
|
|||
|
||||
QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaUsage, Date startDate, Date endDate);
|
||||
|
||||
Pair<List<QuotaSummaryResponse>, Integer> createQuotaSummaryResponse(Boolean listAll);
|
||||
|
||||
Pair<List<QuotaSummaryResponse>, Integer> createQuotaSummaryResponse(Boolean listAll, String keyword, Long startIndex, Long pageSize);
|
||||
|
||||
Pair<List<QuotaSummaryResponse>, Integer> createQuotaSummaryResponse(String accountName, Long domainId);
|
||||
Pair<List<QuotaSummaryResponse>, Integer> createQuotaSummaryResponse(QuotaSummaryCmd cmd);
|
||||
|
||||
QuotaBalanceResponse createQuotaLastBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate);
|
||||
|
||||
|
|
|
|||
|
|
@ -42,11 +42,14 @@ import java.util.stream.Collectors;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.domain.Domain;
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.projects.dao.ProjectDao;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.user.UserVO;
|
||||
import com.cloud.utils.DateUtil;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.command.QuotaBalanceCmd;
|
||||
|
|
@ -56,6 +59,7 @@ import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
|
|||
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaPresetVariablesListCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaStatementCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaSummaryCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaTariffCreateCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaTariffListCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
|
||||
|
|
@ -74,11 +78,13 @@ import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariable
|
|||
import org.apache.cloudstack.quota.activationrule.presetvariables.Value;
|
||||
import org.apache.cloudstack.quota.constant.QuotaConfig;
|
||||
import org.apache.cloudstack.quota.constant.QuotaTypes;
|
||||
|
||||
import org.apache.cloudstack.quota.dao.QuotaAccountDao;
|
||||
import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
|
||||
import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
|
||||
import org.apache.cloudstack.quota.dao.QuotaEmailConfigurationDao;
|
||||
import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
|
||||
import org.apache.cloudstack.quota.dao.QuotaSummaryDao;
|
||||
import org.apache.cloudstack.quota.dao.QuotaTariffDao;
|
||||
import org.apache.cloudstack.quota.dao.QuotaUsageDao;
|
||||
import org.apache.cloudstack.quota.vo.QuotaAccountVO;
|
||||
|
|
@ -86,10 +92,13 @@ import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
|
|||
import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
|
||||
import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
|
||||
import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
|
||||
import org.apache.cloudstack.quota.vo.QuotaSummaryVO;
|
||||
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
|
||||
import org.apache.cloudstack.quota.vo.QuotaUsageVO;
|
||||
import org.apache.cloudstack.utils.jsinterpreter.JsInterpreter;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.compress.utils.Sets;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||
|
|
@ -108,7 +117,6 @@ import com.cloud.user.AccountVO;
|
|||
import com.cloud.user.dao.AccountDao;
|
||||
import com.cloud.user.dao.UserDao;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.Filter;
|
||||
|
||||
@Component
|
||||
public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
|
||||
|
|
@ -121,7 +129,7 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
|
|||
@Inject
|
||||
private QuotaCreditsDao quotaCreditsDao;
|
||||
@Inject
|
||||
private QuotaUsageDao _quotaUsageDao;
|
||||
private QuotaUsageDao quotaUsageDao;
|
||||
@Inject
|
||||
private QuotaEmailTemplatesDao _quotaEmailTemplateDao;
|
||||
|
||||
|
|
@ -132,29 +140,29 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
|
|||
@Inject
|
||||
private AccountDao _accountDao;
|
||||
@Inject
|
||||
private ProjectDao projectDao;
|
||||
@Inject
|
||||
private QuotaAccountDao quotaAccountDao;
|
||||
@Inject
|
||||
private DomainDao _domainDao;
|
||||
private DomainDao domainDao;
|
||||
@Inject
|
||||
private AccountManager _accountMgr;
|
||||
@Inject
|
||||
private QuotaStatement _statement;
|
||||
private QuotaStatement quotaStatement;
|
||||
@Inject
|
||||
private QuotaManager _quotaManager;
|
||||
@Inject
|
||||
private QuotaEmailConfigurationDao quotaEmailConfigurationDao;
|
||||
@Inject
|
||||
private QuotaSummaryDao quotaSummaryDao;
|
||||
@Inject
|
||||
private JsInterpreterHelper jsInterpreterHelper;
|
||||
@Inject
|
||||
private ApiDiscoveryService apiDiscoveryService;
|
||||
|
||||
private final Class<?>[] assignableClasses = {GenericPresetVariable.class, ComputingResources.class};
|
||||
|
||||
protected void checkActivationRulesAllowed(String activationRule) {
|
||||
if (!_quotaService.isJsInterpretationEnabled() && StringUtils.isNotEmpty(activationRule)) {
|
||||
throw new PermissionDeniedException("Quota Tariff Activation Rule cannot be set, as Javascript interpretation is disabled in the configuration.");
|
||||
}
|
||||
}
|
||||
private Set<Account.Type> accountTypesThatCanListAllQuotaSummaries = Sets.newHashSet(Account.Type.ADMIN, Account.Type.DOMAIN_ADMIN);
|
||||
|
||||
@Override
|
||||
public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff, boolean returnActivationRule) {
|
||||
|
|
@ -180,75 +188,113 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Pair<List<QuotaSummaryResponse>, Integer> createQuotaSummaryResponse(final String accountName, final Long domainId) {
|
||||
List<QuotaSummaryResponse> result = new ArrayList<QuotaSummaryResponse>();
|
||||
public Pair<List<QuotaSummaryResponse>, Integer> createQuotaSummaryResponse(QuotaSummaryCmd cmd) {
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
|
||||
if (accountName != null && domainId != null) {
|
||||
Account account = _accountDao.findActiveAccount(accountName, domainId);
|
||||
QuotaSummaryResponse qr = getQuotaSummaryResponse(account);
|
||||
result.add(qr);
|
||||
if (!accountTypesThatCanListAllQuotaSummaries.contains(caller.getType()) || !cmd.isListAll()) {
|
||||
return getQuotaSummaryResponse(cmd.getEntityOwnerId(), null, null, cmd);
|
||||
}
|
||||
|
||||
return new Pair<>(result, result.size());
|
||||
return getQuotaSummaryResponseWithListAll(cmd, caller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<List<QuotaSummaryResponse>, Integer> createQuotaSummaryResponse(Boolean listAll) {
|
||||
return createQuotaSummaryResponse(listAll, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<List<QuotaSummaryResponse>, Integer> createQuotaSummaryResponse(Boolean listAll, final String keyword, final Long startIndex, final Long pageSize) {
|
||||
List<QuotaSummaryResponse> result = new ArrayList<QuotaSummaryResponse>();
|
||||
Integer count = 0;
|
||||
if (listAll) {
|
||||
Filter filter = new Filter(AccountVO.class, "accountName", true, startIndex, pageSize);
|
||||
Pair<List<AccountVO>, Integer> data = _accountDao.findAccountsLike(keyword, filter);
|
||||
count = data.second();
|
||||
for (final AccountVO account : data.first()) {
|
||||
QuotaSummaryResponse qr = getQuotaSummaryResponse(account);
|
||||
result.add(qr);
|
||||
}
|
||||
} else {
|
||||
Pair<List<QuotaAccountVO>, Integer> data = quotaAccountDao.listAllQuotaAccount(startIndex, pageSize);
|
||||
count = data.second();
|
||||
for (final QuotaAccountVO quotaAccount : data.first()) {
|
||||
AccountVO account = _accountDao.findById(quotaAccount.getId());
|
||||
if (account == null) {
|
||||
continue;
|
||||
}
|
||||
QuotaSummaryResponse qr = getQuotaSummaryResponse(account);
|
||||
result.add(qr);
|
||||
protected Pair<List<QuotaSummaryResponse>, Integer> getQuotaSummaryResponseWithListAll(QuotaSummaryCmd cmd, Account caller) {
|
||||
Long domainId = cmd.getDomainId();
|
||||
if (domainId != null) {
|
||||
DomainVO domain = domainDao.findByIdIncludingRemoved(domainId);
|
||||
if (domain == null) {
|
||||
throw new InvalidParameterValueException(String.format("Domain [%s] does not exist.", domainId));
|
||||
}
|
||||
}
|
||||
return new Pair<>(result, count);
|
||||
|
||||
String domainPath = getDomainPathByDomainIdForDomainAdmin(caller);
|
||||
|
||||
Long accountId = cmd.getEntityOwnerId();
|
||||
if (accountId == -1) {
|
||||
accountId = cmd.isListAll() ? null : caller.getAccountId();
|
||||
}
|
||||
|
||||
return getQuotaSummaryResponse(accountId, domainId, domainPath, cmd);
|
||||
}
|
||||
|
||||
protected QuotaSummaryResponse getQuotaSummaryResponse(final Account account) {
|
||||
Calendar[] period = _statement.getCurrentStatementTime();
|
||||
|
||||
if (account != null) {
|
||||
QuotaSummaryResponse qr = new QuotaSummaryResponse();
|
||||
DomainVO domain = _domainDao.findById(account.getDomainId());
|
||||
BigDecimal curBalance = _quotaBalanceDao.lastQuotaBalance(account.getAccountId(), account.getDomainId(), period[1].getTime());
|
||||
BigDecimal quotaUsage = _quotaUsageDao.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, period[0].getTime(), period[1].getTime());
|
||||
|
||||
qr.setAccountId(account.getUuid());
|
||||
qr.setAccountName(account.getAccountName());
|
||||
qr.setDomainId(domain.getUuid());
|
||||
qr.setDomainName(domain.getName());
|
||||
qr.setBalance(curBalance);
|
||||
qr.setQuotaUsage(quotaUsage);
|
||||
qr.setState(account.getState());
|
||||
qr.setStartDate(period[0].getTime());
|
||||
qr.setEndDate(period[1].getTime());
|
||||
qr.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
|
||||
qr.setQuotaEnabled(QuotaConfig.QuotaAccountEnabled.valueIn(account.getId()));
|
||||
qr.setObjectName("summary");
|
||||
return qr;
|
||||
} else {
|
||||
return new QuotaSummaryResponse();
|
||||
/**
|
||||
* Retrieves the domain path of the caller's domain (if the caller is Domain Admin) for filtering in the quota summary query.
|
||||
* @return null if the caller is an Admin or the domain path of the caller's domain if the caller is a Domain Admin.
|
||||
* @throws InvalidParameterValueException if it cannot find the domain.
|
||||
*/
|
||||
protected String getDomainPathByDomainIdForDomainAdmin(Account caller) {
|
||||
if (caller.getType() != Account.Type.DOMAIN_ADMIN) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Long domainId = caller.getDomainId();
|
||||
Domain domain = domainDao.findById(domainId);
|
||||
_accountMgr.checkAccess(caller, domain);
|
||||
|
||||
if (domain == null) {
|
||||
throw new InvalidParameterValueException(String.format("Domain ID [%s] is invalid.", domainId));
|
||||
}
|
||||
|
||||
return domain.getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a <code>List</code> of <code>QuotaSummaryResponse</code> based on the provided parameters.
|
||||
* @param accountId ID of the Account to return the summaries for. If <code>-1</code>, either because no specific
|
||||
* Account was provided, or list all is disabled, then the summary is generated for the calling Account.
|
||||
* @param domainId ID of the Domain to return the summaries for.
|
||||
* @param domainPath path of the Domain to return the summaries for.
|
||||
*/
|
||||
protected Pair<List<QuotaSummaryResponse>, Integer> getQuotaSummaryResponse(Long accountId, Long domainId, String domainPath, QuotaSummaryCmd cmd) {
|
||||
if (accountId != null && accountId == -1) {
|
||||
accountId = CallContext.current().getCallingAccountId();
|
||||
}
|
||||
|
||||
Pair<List<QuotaSummaryVO>, Integer> pairSummaries = quotaSummaryDao.listQuotaSummariesForAccountAndOrDomain(accountId, cmd.getKeyword(), domainId, domainPath,
|
||||
cmd.getAccountStateToShow(), cmd.getStartIndex(), cmd.getPageSizeVal());
|
||||
List<QuotaSummaryVO> summaries = pairSummaries.first();
|
||||
|
||||
if (CollectionUtils.isEmpty(summaries)) {
|
||||
logger.info("There are no summaries to list for parameters [{}].", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(cmd, "accountName", "domainId", "listAll", "page", "pageSize"));
|
||||
return new Pair<>(new ArrayList<>(), 0);
|
||||
}
|
||||
|
||||
List<QuotaSummaryResponse> responses = summaries.stream().map(this::getQuotaSummaryResponse).collect(Collectors.toList());
|
||||
|
||||
return new Pair<>(responses, pairSummaries.second());
|
||||
}
|
||||
|
||||
protected QuotaSummaryResponse getQuotaSummaryResponse(QuotaSummaryVO summary) {
|
||||
QuotaSummaryResponse response = new QuotaSummaryResponse();
|
||||
Account account = _accountDao.findByUuidIncludingRemoved(summary.getAccountUuid());
|
||||
|
||||
Calendar[] period = quotaStatement.getCurrentStatementTime();
|
||||
Date startDate = period[0].getTime();
|
||||
Date endDate = period[1].getTime();
|
||||
BigDecimal quotaUsage = quotaUsageDao.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, startDate, endDate);
|
||||
|
||||
response.setQuotaUsage(quotaUsage);
|
||||
response.setStartDate(startDate);
|
||||
response.setEndDate(endDate);
|
||||
response.setAccountId(summary.getAccountUuid());
|
||||
response.setAccountName(summary.getAccountName());
|
||||
response.setDomainId(summary.getDomainUuid());
|
||||
response.setDomainPath(summary.getDomainPath());
|
||||
response.setBalance(summary.getQuotaBalance());
|
||||
response.setState(summary.getAccountState());
|
||||
response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
|
||||
response.setQuotaEnabled(QuotaConfig.QuotaAccountEnabled.valueIn(account.getId()));
|
||||
response.setDomainRemoved(summary.getDomainRemoved() != null);
|
||||
response.setAccountRemoved(summary.getAccountRemoved() != null);
|
||||
response.setObjectName("summary");
|
||||
|
||||
if (summary.getProjectUuid() != null) {
|
||||
response.setProjectId(summary.getProjectUuid());
|
||||
response.setProjectName(summary.getProjectName());
|
||||
response.setProjectRemoved(summary.getProjectRemoved() != null);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public boolean isUserAllowedToSeeActivationRules(User user) {
|
||||
|
|
@ -450,6 +496,7 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
|
|||
Integer position = cmd.getPosition();
|
||||
|
||||
warnQuotaTariffUpdateDeprecatedFields(cmd);
|
||||
jsInterpreterHelper.ensureInterpreterEnabledIfParameterProvided(ApiConstants.ACTIVATION_RULE, StringUtils.isNotBlank(activationRule));
|
||||
|
||||
QuotaTariffVO currentQuotaTariff = _quotaTariffDao.findByName(name);
|
||||
|
||||
|
|
@ -457,8 +504,6 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
|
|||
throw new InvalidParameterValueException(String.format("There is no quota tariffs with name [%s].", name));
|
||||
}
|
||||
|
||||
checkActivationRulesAllowed(activationRule);
|
||||
|
||||
Date currentQuotaTariffStartDate = currentQuotaTariff.getEffectiveOn();
|
||||
|
||||
currentQuotaTariff.setRemoved(now);
|
||||
|
|
@ -707,14 +752,14 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
|
|||
String activationRule = cmd.getActivationRule();
|
||||
Integer position = ObjectUtils.defaultIfNull(cmd.getPosition(), 1);
|
||||
|
||||
jsInterpreterHelper.ensureInterpreterEnabledIfParameterProvided(ApiConstants.ACTIVATION_RULE, StringUtils.isNotBlank(activationRule));
|
||||
|
||||
QuotaTariffVO currentQuotaTariff = _quotaTariffDao.findByName(name);
|
||||
|
||||
if (currentQuotaTariff != null) {
|
||||
throw new InvalidParameterValueException(String.format("A quota tariff with name [%s] already exist.", name));
|
||||
}
|
||||
|
||||
checkActivationRulesAllowed(activationRule);
|
||||
|
||||
if (startDate.compareTo(now) < 0) {
|
||||
throw new InvalidParameterValueException(String.format("The value passed as Quota tariff's start date is in the past: [%s]. " +
|
||||
"Please, inform a date in the future or do not pass the parameter to use the current date and time.", startDate));
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
package org.apache.cloudstack.api.response;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Date;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
|
@ -30,40 +29,48 @@ import com.cloud.user.Account.State;
|
|||
public class QuotaSummaryResponse extends BaseResponse {
|
||||
|
||||
@SerializedName("accountid")
|
||||
@Param(description = "Account ID")
|
||||
@Param(description = "Account's ID")
|
||||
private String accountId;
|
||||
|
||||
@SerializedName("account")
|
||||
@Param(description = "Account name")
|
||||
@Param(description = "Account's name")
|
||||
private String accountName;
|
||||
|
||||
@SerializedName("domainid")
|
||||
@Param(description = "Domain ID")
|
||||
@Param(description = "Domain's ID")
|
||||
private String domainId;
|
||||
|
||||
@SerializedName("domain")
|
||||
@Param(description = "Domain name")
|
||||
private String domainName;
|
||||
@Param(description = "Domain's path")
|
||||
private String domainPath;
|
||||
|
||||
@SerializedName("balance")
|
||||
@Param(description = "Account balance")
|
||||
@Param(description = "Account's balance")
|
||||
private BigDecimal balance;
|
||||
|
||||
@SerializedName("state")
|
||||
@Param(description = "Account state")
|
||||
@Param(description = "Account's state")
|
||||
private State state;
|
||||
|
||||
@SerializedName("domainremoved")
|
||||
@Param(description = "If the domain is removed or not", since = "4.23.0")
|
||||
private boolean domainRemoved;
|
||||
|
||||
@SerializedName("accountremoved")
|
||||
@Param(description = "If the account is removed or not", since = "4.23.0")
|
||||
private boolean accountRemoved;
|
||||
|
||||
@SerializedName("quota")
|
||||
@Param(description = "Quota usage of this period")
|
||||
@Param(description = "Quota consumed between the startdate and enddate")
|
||||
private BigDecimal quotaUsage;
|
||||
|
||||
@SerializedName("startdate")
|
||||
@Param(description = "Start date")
|
||||
private Date startDate = null;
|
||||
@Param(description = "Start date of the quota consumption")
|
||||
private Date startDate;
|
||||
|
||||
@SerializedName("enddate")
|
||||
@Param(description = "End date")
|
||||
private Date endDate = null;
|
||||
@Param(description = "End date of the quota consumption")
|
||||
private Date endDate;
|
||||
|
||||
@SerializedName("currency")
|
||||
@Param(description = "Currency")
|
||||
|
|
@ -73,9 +80,17 @@ public class QuotaSummaryResponse extends BaseResponse {
|
|||
@Param(description = "If the account has the quota config enabled")
|
||||
private boolean quotaEnabled;
|
||||
|
||||
public QuotaSummaryResponse() {
|
||||
super();
|
||||
}
|
||||
@SerializedName("projectname")
|
||||
@Param(description = "Name of the project", since = "4.23.0")
|
||||
private String projectName;
|
||||
|
||||
@SerializedName("projectid")
|
||||
@Param(description = "Project's id", since = "4.23.0")
|
||||
private String projectId;
|
||||
|
||||
@SerializedName("projectremoved")
|
||||
@Param(description = "Whether the project is removed or not", since = "4.23.0")
|
||||
private Boolean projectRemoved;
|
||||
|
||||
public String getAccountId() {
|
||||
return accountId;
|
||||
|
|
@ -101,28 +116,16 @@ public class QuotaSummaryResponse extends BaseResponse {
|
|||
this.domainId = domainId;
|
||||
}
|
||||
|
||||
public String getDomainName() {
|
||||
return domainName;
|
||||
}
|
||||
|
||||
public void setDomainName(String domainName) {
|
||||
this.domainName = domainName;
|
||||
}
|
||||
|
||||
public BigDecimal getQuotaUsage() {
|
||||
return quotaUsage;
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return state;
|
||||
public void setDomainPath(String domainPath) {
|
||||
this.domainPath = domainPath;
|
||||
}
|
||||
|
||||
public void setState(State state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public void setQuotaUsage(BigDecimal startQuota) {
|
||||
this.quotaUsage = startQuota.setScale(2, RoundingMode.HALF_EVEN);
|
||||
public void setQuotaUsage(BigDecimal quotaUsage) {
|
||||
this.quotaUsage = quotaUsage;
|
||||
}
|
||||
|
||||
public BigDecimal getBalance() {
|
||||
|
|
@ -130,38 +133,42 @@ public class QuotaSummaryResponse extends BaseResponse {
|
|||
}
|
||||
|
||||
public void setBalance(BigDecimal balance) {
|
||||
this.balance = balance.setScale(2, RoundingMode.HALF_EVEN);
|
||||
}
|
||||
|
||||
public Date getStartDate() {
|
||||
return startDate == null ? null : new Date(startDate.getTime());
|
||||
this.balance = balance;
|
||||
}
|
||||
|
||||
public void setStartDate(Date startDate) {
|
||||
this.startDate = startDate == null ? null : new Date(startDate.getTime());
|
||||
}
|
||||
|
||||
public Date getEndDate() {
|
||||
return endDate == null ? null : new Date(endDate.getTime());
|
||||
this.startDate = startDate;
|
||||
}
|
||||
|
||||
public void setEndDate(Date endDate) {
|
||||
this.endDate = endDate == null ? null : new Date(endDate.getTime());
|
||||
}
|
||||
|
||||
public String getCurrency() {
|
||||
return currency;
|
||||
this.endDate = endDate;
|
||||
}
|
||||
|
||||
public void setCurrency(String currency) {
|
||||
this.currency = currency;
|
||||
}
|
||||
|
||||
public boolean getQuotaEnabled() {
|
||||
return quotaEnabled;
|
||||
}
|
||||
|
||||
public void setQuotaEnabled(boolean quotaEnabled) {
|
||||
this.quotaEnabled = quotaEnabled;
|
||||
}
|
||||
|
||||
public void setProjectName(String projectName) {
|
||||
this.projectName = projectName;
|
||||
}
|
||||
|
||||
public void setProjectId(String projectId) {
|
||||
this.projectId = projectId;
|
||||
}
|
||||
|
||||
public void setProjectRemoved(Boolean projectRemoved) {
|
||||
this.projectRemoved = projectRemoved;
|
||||
}
|
||||
|
||||
public void setDomainRemoved(boolean domainRemoved) {
|
||||
this.domainRemoved = domainRemoved;
|
||||
}
|
||||
|
||||
public void setAccountRemoved(boolean accountRemoved) {
|
||||
this.accountRemoved = accountRemoved;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,4 @@ public interface QuotaService extends PluggableService {
|
|||
|
||||
boolean saveQuotaAccount(AccountVO account, BigDecimal aggrUsage, Date endDate);
|
||||
|
||||
boolean isJsInterpretationEnabled();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ import java.util.TimeZone;
|
|||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.projects.ProjectManager;
|
||||
import com.cloud.user.AccountService;
|
||||
import org.apache.cloudstack.api.command.QuotaBalanceCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaConfigureEmailCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaCreditsCmd;
|
||||
|
|
@ -62,7 +64,6 @@ import com.cloud.configuration.Config;
|
|||
import com.cloud.domain.dao.DomainDao;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.server.ManagementService;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountVO;
|
||||
import com.cloud.user.dao.AccountDao;
|
||||
|
|
@ -75,6 +76,8 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
|
|||
@Inject
|
||||
private AccountDao _accountDao;
|
||||
@Inject
|
||||
private AccountService accountService;
|
||||
@Inject
|
||||
private QuotaAccountDao _quotaAcc;
|
||||
@Inject
|
||||
private QuotaUsageDao _quotaUsageDao;
|
||||
|
|
@ -86,11 +89,11 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
|
|||
private QuotaBalanceDao _quotaBalanceDao;
|
||||
@Inject
|
||||
private QuotaResponseBuilder _respBldr;
|
||||
@Inject
|
||||
private ProjectManager projectMgr;
|
||||
|
||||
private TimeZone _usageTimezone;
|
||||
|
||||
private boolean jsInterpretationEnabled = false;
|
||||
|
||||
public QuotaServiceImpl() {
|
||||
super();
|
||||
}
|
||||
|
|
@ -102,8 +105,6 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
|
|||
String timeZoneStr = ObjectUtils.defaultIfNull(_configDao.getValue(Config.UsageAggregationTimezone.toString()), "GMT");
|
||||
_usageTimezone = TimeZone.getTimeZone(timeZoneStr);
|
||||
|
||||
jsInterpretationEnabled = ManagementService.JsInterpretationEnabled.value();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -292,9 +293,4 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
|
|||
_quotaAcc.updateQuotaAccount(accountId, acc);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isJsInterpretationEnabled() {
|
||||
return jsInterpretationEnabled;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,13 +16,11 @@
|
|||
// under the License.
|
||||
package org.apache.cloudstack.api.response;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
|
@ -31,6 +29,7 @@ import java.util.Set;
|
|||
import java.util.HashSet;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.cloud.domain.Domain;
|
||||
import com.cloud.domain.DomainVO;
|
||||
import com.cloud.domain.dao.DomainDao;
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
|
|
@ -43,10 +42,10 @@ import org.apache.cloudstack.api.command.QuotaConfigureEmailCmd;
|
|||
import org.apache.cloudstack.api.command.QuotaCreditsListCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaSummaryCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaValidateActivationRuleCmd;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.discovery.ApiDiscoveryService;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.jsinterpreter.JsInterpreterHelper;
|
||||
import org.apache.cloudstack.quota.QuotaService;
|
||||
import org.apache.cloudstack.quota.QuotaStatement;
|
||||
|
|
@ -79,7 +78,10 @@ import org.junit.runner.RunWith;
|
|||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedConstruction;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.user.Account;
|
||||
|
|
@ -89,8 +91,7 @@ import com.cloud.user.dao.UserDao;
|
|||
import com.cloud.user.User;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class QuotaResponseBuilderImplTest extends TestCase {
|
||||
|
|
@ -153,7 +154,7 @@ public class QuotaResponseBuilderImplTest extends TestCase {
|
|||
Account accountMock;
|
||||
|
||||
@Mock
|
||||
DomainVO domainVOMock;
|
||||
DomainVO domainVoMock;
|
||||
|
||||
@Mock
|
||||
QuotaConfigureEmailCmd quotaConfigureEmailCmdMock;
|
||||
|
|
@ -161,6 +162,9 @@ public class QuotaResponseBuilderImplTest extends TestCase {
|
|||
@Mock
|
||||
QuotaAccountVO quotaAccountVOMock;
|
||||
|
||||
@Mock
|
||||
CallContext callContextMock;
|
||||
|
||||
@Mock
|
||||
QuotaEmailTemplatesVO quotaEmailTemplatesVoMock;
|
||||
|
||||
|
|
@ -184,17 +188,8 @@ public class QuotaResponseBuilderImplTest extends TestCase {
|
|||
CallContext.register(callerUserMock, callerAccountMock);
|
||||
}
|
||||
|
||||
private void overrideDefaultQuotaEnabledConfigValue(final Object value) throws IllegalAccessException, NoSuchFieldException {
|
||||
Field f = ConfigKey.class.getDeclaredField("_defaultValue");
|
||||
f.setAccessible(true);
|
||||
f.set(QuotaConfig.QuotaAccountEnabled, value);
|
||||
}
|
||||
|
||||
private Calendar[] createPeriodForQuotaSummary() {
|
||||
final Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(Calendar.HOUR, 0);
|
||||
return new Calendar[] {calendar, calendar};
|
||||
}
|
||||
@Mock
|
||||
Pair<List<QuotaSummaryResponse>, Integer> quotaSummaryResponseMock1, quotaSummaryResponseMock2;
|
||||
|
||||
@Mock
|
||||
QuotaValidateActivationRuleCmd quotaValidateActivationRuleCmdMock = Mockito.mock(QuotaValidateActivationRuleCmd.class);
|
||||
|
|
@ -466,36 +461,6 @@ public class QuotaResponseBuilderImplTest extends TestCase {
|
|||
Mockito.verify(quotaTariffVoMock).setRemoved(Mockito.any(Date.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getQuotaSummaryResponseTestAccountIsNotNullQuotaIsDisabledShouldReturnFalse() throws NoSuchFieldException, IllegalAccessException {
|
||||
Calendar[] period = createPeriodForQuotaSummary();
|
||||
overrideDefaultQuotaEnabledConfigValue("false");
|
||||
|
||||
Mockito.doReturn(period).when(quotaStatementMock).getCurrentStatementTime();
|
||||
Mockito.doReturn(domainVOMock).when(domainDaoMock).findById(Mockito.anyLong());
|
||||
Mockito.doReturn(BigDecimal.ZERO).when(quotaBalanceDaoMock).lastQuotaBalance(Mockito.anyLong(), Mockito.anyLong(), Mockito.any(Date.class));
|
||||
Mockito.doReturn(BigDecimal.ZERO).when(quotaUsageDaoMock).findTotalQuotaUsage(Mockito.anyLong(), Mockito.anyLong(), Mockito.isNull(), Mockito.any(Date.class), Mockito.any(Date.class));
|
||||
|
||||
QuotaSummaryResponse quotaSummaryResponse = quotaResponseBuilderSpy.getQuotaSummaryResponse(accountMock);
|
||||
|
||||
assertFalse(quotaSummaryResponse.getQuotaEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getQuotaSummaryResponseTestAccountIsNotNullQuotaIsEnabledShouldReturnTrue() throws NoSuchFieldException, IllegalAccessException {
|
||||
Calendar[] period = createPeriodForQuotaSummary();
|
||||
overrideDefaultQuotaEnabledConfigValue("true");
|
||||
|
||||
Mockito.doReturn(period).when(quotaStatementMock).getCurrentStatementTime();
|
||||
Mockito.doReturn(domainVOMock).when(domainDaoMock).findById(Mockito.anyLong());
|
||||
Mockito.doReturn(BigDecimal.ZERO).when(quotaBalanceDaoMock).lastQuotaBalance(Mockito.anyLong(), Mockito.anyLong(), Mockito.any(Date.class));
|
||||
Mockito.doReturn(BigDecimal.ZERO).when(quotaUsageDaoMock).findTotalQuotaUsage(Mockito.anyLong(), Mockito.anyLong(), Mockito.isNull(), Mockito.any(Date.class), Mockito.any(Date.class));
|
||||
|
||||
QuotaSummaryResponse quotaSummaryResponse = quotaResponseBuilderSpy.getQuotaSummaryResponse(accountMock);
|
||||
|
||||
assertTrue(quotaSummaryResponse.getQuotaEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterSupportedTypesTestReturnWhenQuotaTypeDoesNotMatch() throws NoSuchFieldException {
|
||||
List<Pair<String, String>> variables = new ArrayList<>();
|
||||
|
|
@ -576,6 +541,63 @@ public class QuotaResponseBuilderImplTest extends TestCase {
|
|||
quotaResponseBuilderSpy.validateQuotaConfigureEmailCmdParameters(quotaConfigureEmailCmdMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createQuotaSummaryResponseTestNotListAllAndAllAccountTypesReturnsSingleRecord() {
|
||||
QuotaSummaryCmd cmd = new QuotaSummaryCmd();
|
||||
|
||||
try(MockedStatic<CallContext> callContextMocked = Mockito.mockStatic(CallContext.class)) {
|
||||
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
|
||||
|
||||
Mockito.doReturn(accountMock).when(callContextMock).getCallingAccount();
|
||||
|
||||
Mockito.doReturn(quotaSummaryResponseMock1).when(quotaResponseBuilderSpy).getQuotaSummaryResponse(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
|
||||
|
||||
for (Account.Type type : Account.Type.values()) {
|
||||
Mockito.doReturn(type).when(accountMock).getType();
|
||||
|
||||
Pair<List<QuotaSummaryResponse>, Integer> result = quotaResponseBuilderSpy.createQuotaSummaryResponse(cmd);
|
||||
Assert.assertEquals(quotaSummaryResponseMock1, result);
|
||||
}
|
||||
|
||||
Mockito.verify(quotaResponseBuilderSpy, Mockito.times(Account.Type.values().length)).getQuotaSummaryResponse(Mockito.any(), Mockito.any(), Mockito.any(),
|
||||
Mockito.any());
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDomainPathByDomainIdForDomainAdminTestAccountNotDomainAdminReturnsNull() {
|
||||
for (Account.Type type : Account.Type.values()) {
|
||||
if (Account.Type.DOMAIN_ADMIN.equals(type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Mockito.doReturn(type).when(accountMock).getType();
|
||||
Assert.assertNull(quotaResponseBuilderSpy.getDomainPathByDomainIdForDomainAdmin(accountMock));
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void getDomainPathByDomainIdForDomainAdminTestDomainFromCallerIsNullThrowsInvalidParameterValueException() {
|
||||
Mockito.doReturn(Account.Type.DOMAIN_ADMIN).when(accountMock).getType();
|
||||
Mockito.doReturn(null).when(domainDaoMock).findById(Mockito.anyLong());
|
||||
Mockito.lenient().doNothing().when(accountManagerMock).checkAccess(Mockito.any(Account.class), Mockito.any(Domain.class));
|
||||
|
||||
quotaResponseBuilderSpy.getDomainPathByDomainIdForDomainAdmin(accountMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDomainPathByDomainIdForDomainAdminTestDomainFromCallerIsNotNullReturnsPath() {
|
||||
String expected = "/test/";
|
||||
|
||||
Mockito.doReturn(Account.Type.DOMAIN_ADMIN).when(accountMock).getType();
|
||||
Mockito.doReturn(domainVoMock).when(domainDaoMock).findById(Mockito.anyLong());
|
||||
Mockito.doNothing().when(accountManagerMock).checkAccess(Mockito.any(Account.class), Mockito.any(Domain.class));
|
||||
Mockito.doReturn(expected).when(domainVoMock).getPath();
|
||||
|
||||
String result = quotaResponseBuilderSpy.getDomainPathByDomainIdForDomainAdmin(accountMock);
|
||||
Assert.assertEquals(expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getQuotaEmailConfigurationVoTestTemplateNameIsNull() {
|
||||
Mockito.doReturn(null).when(quotaConfigureEmailCmdMock).getTemplateName();
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package org.apache.cloudstack.quota;
|
|||
|
||||
import com.cloud.configuration.Config;
|
||||
import com.cloud.domain.dao.DomainDao;
|
||||
import com.cloud.user.AccountVO;
|
||||
import com.cloud.user.dao.AccountDao;
|
||||
import com.cloud.utils.db.TransactionLegacy;
|
||||
import junit.framework.TestCase;
|
||||
|
|
@ -33,8 +34,10 @@ import org.joda.time.DateTime;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import javax.naming.ConfigurationException;
|
||||
|
|
@ -48,7 +51,7 @@ import java.util.List;
|
|||
public class QuotaServiceImplTest extends TestCase {
|
||||
|
||||
@Mock
|
||||
AccountDao accountDao;
|
||||
AccountDao accountDaoMock;
|
||||
@Mock
|
||||
QuotaAccountDao quotaAcc;
|
||||
@Mock
|
||||
|
|
@ -61,8 +64,13 @@ public class QuotaServiceImplTest extends TestCase {
|
|||
QuotaBalanceDao quotaBalanceDao;
|
||||
@Mock
|
||||
QuotaResponseBuilder respBldr;
|
||||
@Mock
|
||||
private AccountVO accountVoMock;
|
||||
|
||||
@Spy
|
||||
@InjectMocks
|
||||
QuotaServiceImpl quotaServiceImplSpy;
|
||||
|
||||
QuotaServiceImpl quotaService = new QuotaServiceImpl();
|
||||
|
||||
@Before
|
||||
public void setup() throws IllegalAccessException, NoSuchFieldException, ConfigurationException {
|
||||
|
|
@ -71,34 +79,34 @@ public class QuotaServiceImplTest extends TestCase {
|
|||
|
||||
Field accountDaoField = QuotaServiceImpl.class.getDeclaredField("_accountDao");
|
||||
accountDaoField.setAccessible(true);
|
||||
accountDaoField.set(quotaService, accountDao);
|
||||
accountDaoField.set(quotaServiceImplSpy, accountDaoMock);
|
||||
|
||||
Field quotaAccountDaoField = QuotaServiceImpl.class.getDeclaredField("_quotaAcc");
|
||||
quotaAccountDaoField.setAccessible(true);
|
||||
quotaAccountDaoField.set(quotaService, quotaAcc);
|
||||
quotaAccountDaoField.set(quotaServiceImplSpy, quotaAcc);
|
||||
|
||||
Field quotaUsageDaoField = QuotaServiceImpl.class.getDeclaredField("_quotaUsageDao");
|
||||
quotaUsageDaoField.setAccessible(true);
|
||||
quotaUsageDaoField.set(quotaService, quotaUsageDao);
|
||||
quotaUsageDaoField.set(quotaServiceImplSpy, quotaUsageDao);
|
||||
|
||||
Field domainDaoField = QuotaServiceImpl.class.getDeclaredField("_domainDao");
|
||||
domainDaoField.setAccessible(true);
|
||||
domainDaoField.set(quotaService, domainDao);
|
||||
domainDaoField.set(quotaServiceImplSpy, domainDao);
|
||||
|
||||
Field configDaoField = QuotaServiceImpl.class.getDeclaredField("_configDao");
|
||||
configDaoField.setAccessible(true);
|
||||
configDaoField.set(quotaService, configDao);
|
||||
configDaoField.set(quotaServiceImplSpy, configDao);
|
||||
|
||||
Field balanceDaoField = QuotaServiceImpl.class.getDeclaredField("_quotaBalanceDao");
|
||||
balanceDaoField.setAccessible(true);
|
||||
balanceDaoField.set(quotaService, quotaBalanceDao);
|
||||
balanceDaoField.set(quotaServiceImplSpy, quotaBalanceDao);
|
||||
|
||||
Field QuotaResponseBuilderField = QuotaServiceImpl.class.getDeclaredField("_respBldr");
|
||||
QuotaResponseBuilderField.setAccessible(true);
|
||||
QuotaResponseBuilderField.set(quotaService, respBldr);
|
||||
QuotaResponseBuilderField.set(quotaServiceImplSpy, respBldr);
|
||||
|
||||
Mockito.when(configDao.getValue(Mockito.eq(Config.UsageAggregationTimezone.toString()))).thenReturn("IST");
|
||||
quotaService.configure("randomName", null);
|
||||
quotaServiceImplSpy.configure("randomName", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -120,9 +128,9 @@ public class QuotaServiceImplTest extends TestCase {
|
|||
Mockito.when(quotaBalanceDao.lastQuotaBalanceVO(Mockito.eq(accountId), Mockito.eq(domainId), Mockito.any(Date.class))).thenReturn(records);
|
||||
|
||||
// with enddate
|
||||
assertTrue(quotaService.findQuotaBalanceVO(accountId, accountName, domainId, startDate, endDate).get(0).equals(qb));
|
||||
assertTrue(quotaServiceImplSpy.findQuotaBalanceVO(accountId, accountName, domainId, startDate, endDate).get(0).equals(qb));
|
||||
// without enddate
|
||||
assertTrue(quotaService.findQuotaBalanceVO(accountId, accountName, domainId, startDate, null).get(0).equals(qb));
|
||||
assertTrue(quotaServiceImplSpy.findQuotaBalanceVO(accountId, accountName, domainId, startDate, null).get(0).equals(qb));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -133,7 +141,7 @@ public class QuotaServiceImplTest extends TestCase {
|
|||
final Date startDate = new DateTime().minusDays(2).toDate();
|
||||
final Date endDate = new Date();
|
||||
|
||||
quotaService.getQuotaUsage(accountId, accountName, domainId, QuotaTypes.IP_ADDRESS, startDate, endDate);
|
||||
quotaServiceImplSpy.getQuotaUsage(accountId, accountName, domainId, QuotaTypes.IP_ADDRESS, startDate, endDate);
|
||||
Mockito.verify(quotaUsageDao, Mockito.times(1)).findQuotaUsage(Mockito.eq(accountId), Mockito.eq(domainId), Mockito.eq(QuotaTypes.IP_ADDRESS), Mockito.any(Date.class), Mockito.any(Date.class));
|
||||
}
|
||||
|
||||
|
|
@ -142,13 +150,13 @@ public class QuotaServiceImplTest extends TestCase {
|
|||
// existing account
|
||||
QuotaAccountVO quotaAccountVO = new QuotaAccountVO();
|
||||
Mockito.when(quotaAcc.findByIdQuotaAccount(Mockito.anyLong())).thenReturn(quotaAccountVO);
|
||||
quotaService.setLockAccount(2L, true);
|
||||
quotaServiceImplSpy.setLockAccount(2L, true);
|
||||
Mockito.verify(quotaAcc, Mockito.times(0)).persistQuotaAccount(Mockito.any(QuotaAccountVO.class));
|
||||
Mockito.verify(quotaAcc, Mockito.times(1)).updateQuotaAccount(Mockito.anyLong(), Mockito.any(QuotaAccountVO.class));
|
||||
|
||||
// new account
|
||||
Mockito.when(quotaAcc.findByIdQuotaAccount(Mockito.anyLong())).thenReturn(null);
|
||||
quotaService.setLockAccount(2L, true);
|
||||
quotaServiceImplSpy.setLockAccount(2L, true);
|
||||
Mockito.verify(quotaAcc, Mockito.times(1)).persistQuotaAccount(Mockito.any(QuotaAccountVO.class));
|
||||
}
|
||||
|
||||
|
|
@ -160,13 +168,14 @@ public class QuotaServiceImplTest extends TestCase {
|
|||
// existing account setting
|
||||
QuotaAccountVO quotaAccountVO = new QuotaAccountVO();
|
||||
Mockito.when(quotaAcc.findByIdQuotaAccount(Mockito.anyLong())).thenReturn(quotaAccountVO);
|
||||
quotaService.setMinBalance(accountId, balance);
|
||||
quotaServiceImplSpy.setMinBalance(accountId, balance);
|
||||
Mockito.verify(quotaAcc, Mockito.times(0)).persistQuotaAccount(Mockito.any(QuotaAccountVO.class));
|
||||
Mockito.verify(quotaAcc, Mockito.times(1)).updateQuotaAccount(Mockito.anyLong(), Mockito.any(QuotaAccountVO.class));
|
||||
|
||||
// no account with limit set
|
||||
Mockito.when(quotaAcc.findByIdQuotaAccount(Mockito.anyLong())).thenReturn(null);
|
||||
quotaService.setMinBalance(accountId, balance);
|
||||
quotaServiceImplSpy.setMinBalance(accountId, balance);
|
||||
Mockito.verify(quotaAcc, Mockito.times(1)).persistQuotaAccount(Mockito.any(QuotaAccountVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,28 +79,47 @@ public class LibvirtGpuDef {
|
|||
gpuBuilder.append(" <driver name='vfio'/>\n");
|
||||
gpuBuilder.append(" <source>\n");
|
||||
|
||||
// Parse the bus address (e.g., 00:02.0) into domain, bus, slot, function
|
||||
String domain = "0x0000";
|
||||
String bus = "0x00";
|
||||
String slot = "0x00";
|
||||
String function = "0x0";
|
||||
// Parse the bus address into domain, bus, slot, function. Two input formats are accepted:
|
||||
// - "dddd:bb:ss.f" full PCI address with domain (e.g. 0000:00:02.0)
|
||||
// - "bb:ss.f" legacy short BDF; domain defaults to 0000
|
||||
// Each segment is parsed as a hex integer and formatted with fixed widths
|
||||
// (domain: 4 hex digits, bus/slot: 2 hex digits, function: 1 hex digit) to
|
||||
// produce canonical libvirt XML values regardless of input casing or padding.
|
||||
int domainVal = 0, busVal = 0, slotVal = 0, funcVal = 0;
|
||||
|
||||
if (busAddress != null && !busAddress.isEmpty()) {
|
||||
String[] parts = busAddress.split(":");
|
||||
if (parts.length > 1) {
|
||||
bus = "0x" + parts[0];
|
||||
String[] slotFunctionParts = parts[1].split("\\.");
|
||||
if (slotFunctionParts.length > 0) {
|
||||
slot = "0x" + slotFunctionParts[0];
|
||||
if (slotFunctionParts.length > 1) {
|
||||
function = "0x" + slotFunctionParts[1].trim();
|
||||
}
|
||||
try {
|
||||
String slotFunction;
|
||||
if (parts.length == 3) {
|
||||
domainVal = Integer.parseInt(parts[0], 16);
|
||||
busVal = Integer.parseInt(parts[1], 16);
|
||||
slotFunction = parts[2];
|
||||
} else if (parts.length == 2) {
|
||||
busVal = Integer.parseInt(parts[0], 16);
|
||||
slotFunction = parts[1];
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid PCI bus address format: '" + busAddress + "'");
|
||||
}
|
||||
String[] sf = slotFunction.split("\\.");
|
||||
if (sf.length == 2) {
|
||||
slotVal = Integer.parseInt(sf[0], 16);
|
||||
funcVal = Integer.parseInt(sf[1].trim(), 16);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid PCI bus address format: '" + busAddress + "'");
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Invalid PCI bus address format: '" + busAddress + "'", e);
|
||||
}
|
||||
}
|
||||
|
||||
String domain = String.format("0x%04x", domainVal);
|
||||
String bus = String.format("0x%02x", busVal);
|
||||
String slot = String.format("0x%02x", slotVal);
|
||||
String function = String.format("0x%x", funcVal);
|
||||
|
||||
gpuBuilder.append(" <address domain='").append(domain).append("' bus='").append(bus).append("' slot='")
|
||||
.append(slot).append("' function='").append(function.trim()).append("'/>\n");
|
||||
.append(slot).append("' function='").append(function).append("'/>\n");
|
||||
gpuBuilder.append(" </source>\n");
|
||||
gpuBuilder.append("</hostdev>\n");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,10 @@ import java.util.Map;
|
|||
@ResourceWrapper(handles = CheckVolumeCommand.class)
|
||||
public final class LibvirtCheckVolumeCommandWrapper extends CommandWrapper<CheckVolumeCommand, Answer, LibvirtComputingResource> {
|
||||
|
||||
private static final List<Storage.StoragePoolType> STORAGE_POOL_TYPES_SUPPORTED = Arrays.asList(Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.NetworkFilesystem);
|
||||
private static final List<Storage.StoragePoolType> STORAGE_POOL_TYPES_SUPPORTED = Arrays.asList(
|
||||
Storage.StoragePoolType.Filesystem,
|
||||
Storage.StoragePoolType.NetworkFilesystem,
|
||||
Storage.StoragePoolType.SharedMountPoint);
|
||||
|
||||
@Override
|
||||
public Answer execute(final CheckVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ import java.util.stream.Collectors;
|
|||
public final class LibvirtGetVolumesOnStorageCommandWrapper extends CommandWrapper<GetVolumesOnStorageCommand, Answer, LibvirtComputingResource> {
|
||||
|
||||
static final List<StoragePoolType> STORAGE_POOL_TYPES_SUPPORTED_BY_QEMU_IMG = Arrays.asList(StoragePoolType.NetworkFilesystem,
|
||||
StoragePoolType.Filesystem, StoragePoolType.RBD);
|
||||
StoragePoolType.Filesystem, StoragePoolType.RBD, StoragePoolType.SharedMountPoint);
|
||||
|
||||
@Override
|
||||
public Answer execute(final GetVolumesOnStorageCommand command, final LibvirtComputingResource libvirtComputingResource) {
|
||||
|
|
|
|||
|
|
@ -41,9 +41,9 @@ import org.apache.commons.lang3.RandomStringUtils;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.libvirt.LibvirtException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
|
@ -56,10 +56,25 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper<RestoreBa
|
|||
private static final String UMOUNT_COMMAND = "sudo umount %s";
|
||||
private static final String FILE_PATH_PLACEHOLDER = "%s/%s";
|
||||
private static final String ATTACH_QCOW2_DISK_COMMAND = " virsh attach-disk %s %s %s --driver qemu --subdriver qcow2 --cache none";
|
||||
private static final String ATTACH_RAW_DISK_COMMAND = " virsh attach-disk %s %s %s --driver qemu --cache none";
|
||||
private static final String ATTACH_RBD_DISK_XML_COMMAND = " virsh attach-device %s /dev/stdin <<EOF%sEOF";
|
||||
private static final String CURRRENT_DEVICE = "virsh domblklist --domain %s | tail -n 3 | head -n 1 | awk '{print $1}'";
|
||||
private static final String RSYNC_COMMAND = "rsync -az %s %s";
|
||||
|
||||
private String getVolumeUuidFromPath(String volumePath, PrimaryDataStoreTO volumePool) {
|
||||
if (Storage.StoragePoolType.Linstor.equals(volumePool.getPoolType())) {
|
||||
Path path = Paths.get(volumePath);
|
||||
String rscName = path.getParent().getFileName().toString();
|
||||
if (rscName.startsWith("cs-")) {
|
||||
rscName = rscName.substring(3);
|
||||
}
|
||||
return rscName;
|
||||
} else {
|
||||
int lastIndex = volumePath.lastIndexOf("/");
|
||||
return volumePath.substring(lastIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Answer execute(RestoreBackupCommand command, LibvirtComputingResource serverResource) {
|
||||
String vmName = command.getVmName();
|
||||
|
|
@ -73,7 +88,7 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper<RestoreBa
|
|||
List<PrimaryDataStoreTO> restoreVolumePools = command.getRestoreVolumePools();
|
||||
List<String> restoreVolumePaths = command.getRestoreVolumePaths();
|
||||
Integer mountTimeout = command.getMountTimeout() * 1000;
|
||||
int timeout = command.getWait();
|
||||
int timeout = command.getWait() > 0 ? command.getWait() * 1000 : serverResource.getCmdsTimeout();
|
||||
KVMStoragePoolManager storagePoolMgr = serverResource.getStoragePoolMgr();
|
||||
List<String> backupFiles = command.getBackupFiles();
|
||||
|
||||
|
|
@ -84,9 +99,9 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper<RestoreBa
|
|||
PrimaryDataStoreTO volumePool = restoreVolumePools.get(0);
|
||||
String volumePath = restoreVolumePaths.get(0);
|
||||
String backupFile = backupFiles.get(0);
|
||||
int lastIndex = volumePath.lastIndexOf("/");
|
||||
newVolumeId = volumePath.substring(lastIndex + 1);
|
||||
restoreVolume(storagePoolMgr, backupPath, volumePool, volumePath, diskType, backupFile,
|
||||
newVolumeId = getVolumeUuidFromPath(volumePath, volumePool);
|
||||
Long size = command.getRestoreVolumeSizes().get(0);
|
||||
restoreVolume(storagePoolMgr, backupPath, volumePool, volumePath, diskType, backupFile, size,
|
||||
new Pair<>(vmName, command.getVmState()), mountDirectory, timeout);
|
||||
} else if (Boolean.TRUE.equals(vmExists)) {
|
||||
restoreVolumesOfExistingVM(storagePoolMgr, restoreVolumePools, restoreVolumePaths, backedVolumeUUIDs, backupPath, backupFiles, mountDirectory, timeout);
|
||||
|
|
@ -143,7 +158,7 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper<RestoreBa
|
|||
String volumePath = volumePaths.get(i);
|
||||
String backupFile = backupFiles.get(i);
|
||||
String bkpPath = getBackupPath(mountDirectory, backupPath, backupFile, diskType);
|
||||
String volumeUuid = volumePath.substring(volumePath.lastIndexOf(File.separator) + 1);
|
||||
String volumeUuid = getVolumeUuidFromPath(volumePath, volumePool);
|
||||
diskType = "datadisk";
|
||||
verifyBackupFile(bkpPath, volumeUuid);
|
||||
if (!replaceVolumeWithBackup(storagePoolMgr, volumePool, volumePath, bkpPath, timeout)) {
|
||||
|
|
@ -157,14 +172,14 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper<RestoreBa
|
|||
}
|
||||
|
||||
private void restoreVolume(KVMStoragePoolManager storagePoolMgr, String backupPath, PrimaryDataStoreTO volumePool, String volumePath, String diskType, String backupFile,
|
||||
Pair<String, VirtualMachine.State> vmNameAndState, String mountDirectory, int timeout) {
|
||||
Long size, Pair<String, VirtualMachine.State> vmNameAndState, String mountDirectory, int timeout) {
|
||||
String bkpPath;
|
||||
String volumeUuid;
|
||||
try {
|
||||
bkpPath = getBackupPath(mountDirectory, backupPath, backupFile, diskType);
|
||||
volumeUuid = volumePath.substring(volumePath.lastIndexOf(File.separator) + 1);
|
||||
volumeUuid = getVolumeUuidFromPath(volumePath, volumePool);
|
||||
verifyBackupFile(bkpPath, volumeUuid);
|
||||
if (!replaceVolumeWithBackup(storagePoolMgr, volumePool, volumePath, bkpPath, timeout, true)) {
|
||||
if (!replaceVolumeWithBackup(storagePoolMgr, volumePool, volumePath, bkpPath, timeout, true, size)) {
|
||||
throw new CloudRuntimeException(String.format("Unable to restore contents from the backup volume [%s].", volumeUuid));
|
||||
|
||||
}
|
||||
|
|
@ -247,42 +262,66 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper<RestoreBa
|
|||
}
|
||||
|
||||
private boolean replaceVolumeWithBackup(KVMStoragePoolManager storagePoolMgr, PrimaryDataStoreTO volumePool, String volumePath, String backupPath, int timeout) {
|
||||
return replaceVolumeWithBackup(storagePoolMgr, volumePool, volumePath, backupPath, timeout, false);
|
||||
return replaceVolumeWithBackup(storagePoolMgr, volumePool, volumePath, backupPath, timeout, false, null);
|
||||
}
|
||||
|
||||
private boolean replaceVolumeWithBackup(KVMStoragePoolManager storagePoolMgr, PrimaryDataStoreTO volumePool, String volumePath, String backupPath, int timeout, boolean createTargetVolume) {
|
||||
if (volumePool.getPoolType() != Storage.StoragePoolType.RBD) {
|
||||
int exitValue = Script.runSimpleBashScriptForExitValue(String.format(RSYNC_COMMAND, backupPath, volumePath));
|
||||
return exitValue == 0;
|
||||
private boolean replaceVolumeWithBackup(KVMStoragePoolManager storagePoolMgr, PrimaryDataStoreTO volumePool, String volumePath, String backupPath, int timeout, boolean createTargetVolume, Long size) {
|
||||
if (List.of(Storage.StoragePoolType.RBD, Storage.StoragePoolType.Linstor).contains(volumePool.getPoolType())) {
|
||||
return replaceBlockDeviceWithBackup(storagePoolMgr, volumePool, volumePath, backupPath, timeout, createTargetVolume, size);
|
||||
}
|
||||
|
||||
return replaceRbdVolumeWithBackup(storagePoolMgr, volumePool, volumePath, backupPath, timeout, createTargetVolume);
|
||||
int exitValue = Script.runSimpleBashScriptForExitValue(String.format(RSYNC_COMMAND, backupPath, volumePath), timeout, false);
|
||||
return exitValue == 0;
|
||||
}
|
||||
|
||||
private boolean replaceRbdVolumeWithBackup(KVMStoragePoolManager storagePoolMgr, PrimaryDataStoreTO volumePool, String volumePath, String backupPath, int timeout, boolean createTargetVolume) {
|
||||
private boolean replaceBlockDeviceWithBackup(KVMStoragePoolManager storagePoolMgr, PrimaryDataStoreTO volumePool, String volumePath, String backupPath, int timeout, boolean createTargetVolume, Long size) {
|
||||
KVMStoragePool volumeStoragePool = storagePoolMgr.getStoragePool(volumePool.getPoolType(), volumePool.getUuid());
|
||||
QemuImg qemu;
|
||||
try {
|
||||
qemu = new QemuImg(timeout * 1000, true, false);
|
||||
if (!createTargetVolume) {
|
||||
KVMPhysicalDisk rdbDisk = volumeStoragePool.getPhysicalDisk(volumePath);
|
||||
logger.debug("Restoring RBD volume: {}", rdbDisk.toString());
|
||||
qemu = new QemuImg(timeout, true, false);
|
||||
String volumeUuid = getVolumeUuidFromPath(volumePath, volumePool);
|
||||
KVMPhysicalDisk disk = null;
|
||||
if (createTargetVolume) {
|
||||
if (Storage.StoragePoolType.Linstor.equals(volumePool.getPoolType())) {
|
||||
if (size == null) {
|
||||
throw new CloudRuntimeException("Restore volume size is required for Linstor pool when creating target volume");
|
||||
}
|
||||
disk = volumeStoragePool.createPhysicalDisk(volumeUuid, QemuImg.PhysicalDiskFormat.RAW, Storage.ProvisioningType.THIN, size, null);
|
||||
}
|
||||
} else {
|
||||
if (Storage.StoragePoolType.Linstor.equals(volumePool.getPoolType())) {
|
||||
storagePoolMgr.connectPhysicalDisk(volumePool.getPoolType(), volumePool.getUuid(), volumeUuid, null);
|
||||
} else {
|
||||
disk = volumeStoragePool.getPhysicalDisk(volumePath);
|
||||
}
|
||||
qemu.setSkipTargetVolumeCreation(true);
|
||||
}
|
||||
if (disk != null) {
|
||||
logger.debug("Restoring volume: {}", disk.toString());
|
||||
}
|
||||
} catch (LibvirtException ex) {
|
||||
throw new CloudRuntimeException("Failed to create qemu-img command to restore RBD volume with backup", ex);
|
||||
throw new CloudRuntimeException(String.format("Failed to create qemu-img command to restore %s volume with backup", volumePool.getPoolType()), ex);
|
||||
}
|
||||
|
||||
QemuImgFile srcBackupFile = null;
|
||||
QemuImgFile destVolumeFile = null;
|
||||
try {
|
||||
srcBackupFile = new QemuImgFile(backupPath, QemuImg.PhysicalDiskFormat.QCOW2);
|
||||
String rbdDestVolumeFile = KVMPhysicalDisk.RBDStringBuilder(volumeStoragePool, volumePath);
|
||||
destVolumeFile = new QemuImgFile(rbdDestVolumeFile, QemuImg.PhysicalDiskFormat.RAW);
|
||||
|
||||
logger.debug("Starting convert backup {} to RBD volume {}", backupPath, volumePath);
|
||||
String destVolume;
|
||||
switch(volumePool.getPoolType()) {
|
||||
case Linstor:
|
||||
destVolume = volumePath;
|
||||
break;
|
||||
case RBD:
|
||||
destVolume = KVMPhysicalDisk.RBDStringBuilder(volumeStoragePool, volumePath);
|
||||
break;
|
||||
default:
|
||||
throw new CloudRuntimeException(String.format("Unsupported storage pool type [%s] for block device restore with backup.", volumePool.getPoolType()));
|
||||
}
|
||||
destVolumeFile = new QemuImgFile(destVolume, QemuImg.PhysicalDiskFormat.RAW);
|
||||
logger.debug("Starting convert backup {} to volume {}", backupPath, volumePath);
|
||||
qemu.convert(srcBackupFile, destVolumeFile);
|
||||
logger.debug("Successfully converted backup {} to RBD volume {}", backupPath, volumePath);
|
||||
logger.debug("Successfully converted backup {} to volume {}", backupPath, volumePath);
|
||||
} catch (QemuImgException | LibvirtException e) {
|
||||
String srcFilename = srcBackupFile != null ? srcBackupFile.getFileName() : null;
|
||||
String destFilename = destVolumeFile != null ? destVolumeFile.getFileName() : null;
|
||||
|
|
@ -296,12 +335,14 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper<RestoreBa
|
|||
private boolean attachVolumeToVm(KVMStoragePoolManager storagePoolMgr, String vmName, PrimaryDataStoreTO volumePool, String volumePath) {
|
||||
String deviceToAttachDiskTo = getDeviceToAttachDisk(vmName);
|
||||
int exitValue;
|
||||
if (volumePool.getPoolType() != Storage.StoragePoolType.RBD) {
|
||||
exitValue = Script.runSimpleBashScriptForExitValue(String.format(ATTACH_QCOW2_DISK_COMMAND, vmName, volumePath, deviceToAttachDiskTo));
|
||||
} else {
|
||||
if (volumePool.getPoolType() == Storage.StoragePoolType.RBD) {
|
||||
String xmlForRbdDisk = getXmlForRbdDisk(storagePoolMgr, volumePool, volumePath, deviceToAttachDiskTo);
|
||||
logger.debug("RBD disk xml to attach: {}", xmlForRbdDisk);
|
||||
exitValue = Script.runSimpleBashScriptForExitValue(String.format(ATTACH_RBD_DISK_XML_COMMAND, vmName, xmlForRbdDisk));
|
||||
} else if (volumePool.getPoolType() == Storage.StoragePoolType.Linstor) {
|
||||
exitValue = Script.runSimpleBashScriptForExitValue(String.format(ATTACH_RAW_DISK_COMMAND, vmName, volumePath, deviceToAttachDiskTo));
|
||||
} else {
|
||||
exitValue = Script.runSimpleBashScriptForExitValue(String.format(ATTACH_QCOW2_DISK_COMMAND, vmName, volumePath, deviceToAttachDiskTo));
|
||||
}
|
||||
return exitValue == 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ public class LibvirtTakeBackupCommandWrapper extends CommandWrapper<TakeBackupCo
|
|||
List<PrimaryDataStoreTO> volumePools = command.getVolumePools();
|
||||
final List<String> volumePaths = command.getVolumePaths();
|
||||
KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr();
|
||||
int timeout = command.getWait() > 0 ? command.getWait() * 1000 : libvirtComputingResource.getCmdsTimeout();
|
||||
|
||||
List<String> diskPaths = new ArrayList<>();
|
||||
if (Objects.nonNull(volumePaths)) {
|
||||
|
|
@ -81,7 +82,7 @@ public class LibvirtTakeBackupCommandWrapper extends CommandWrapper<TakeBackupCo
|
|||
"-d", diskPaths.isEmpty() ? "" : String.join(",", diskPaths)
|
||||
});
|
||||
|
||||
Pair<Integer, String> result = Script.executePipedCommands(commands, libvirtComputingResource.getCmdsTimeout());
|
||||
Pair<Integer, String> result = Script.executePipedCommands(commands, timeout);
|
||||
|
||||
if (result.first() != 0) {
|
||||
logger.debug("Failed to take VM backup: " + result.second());
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import com.cloud.agent.api.PrepareStorageClientCommand;
|
||||
import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClient;
|
||||
|
|
@ -644,18 +645,30 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor {
|
|||
// Assuming SDC service is started, add mdms
|
||||
String mdms = details.get(ScaleIOGatewayClient.STORAGE_POOL_MDMS);
|
||||
String[] mdmAddresses = mdms.split(",");
|
||||
if (mdmAddresses.length > 0) {
|
||||
if (ScaleIOUtil.isMdmPresent(mdmAddresses[0])) {
|
||||
return new Ternary<>(true, getSDCDetails(details), "MDM added, no need to prepare the SDC client");
|
||||
}
|
||||
|
||||
ScaleIOUtil.addMdms(mdmAddresses);
|
||||
if (!ScaleIOUtil.isMdmPresent(mdmAddresses[0])) {
|
||||
return new Ternary<>(false, null, "Failed to add MDMs");
|
||||
// remove MDMs already present in the config and added to the SDC
|
||||
String[] mdmAddressesToAdd = Arrays.stream(mdmAddresses)
|
||||
.filter(Predicate.not(ScaleIOUtil::isMdmPresent))
|
||||
.toArray(String[]::new);
|
||||
// if all MDMs are already in the config and added to the SDC
|
||||
if (mdmAddressesToAdd.length < 1 && mdmAddresses.length > 0) {
|
||||
String msg = String.format("MDMs %s of the storage pool %s are already added", mdms, uuid);
|
||||
logger.debug(msg);
|
||||
return new Ternary<>(true, getSDCDetails(details), msg);
|
||||
} else if (mdmAddressesToAdd.length > 0) {
|
||||
ScaleIOUtil.addMdms(mdmAddressesToAdd);
|
||||
String[] missingMdmAddresses = Arrays.stream(mdmAddressesToAdd)
|
||||
.filter(Predicate.not(ScaleIOUtil::isMdmPresent))
|
||||
.toArray(String[]::new);
|
||||
if (missingMdmAddresses.length > 0) {
|
||||
String msg = String.format("Failed to add MDMs %s of the storage pool %s", String.join(", ", missingMdmAddresses), uuid);
|
||||
logger.debug(msg);
|
||||
return new Ternary<>(false, null, msg);
|
||||
} else {
|
||||
logger.debug(String.format("MDMs %s added to storage pool %s", mdms, uuid));
|
||||
logger.debug("MDMs {} of the storage pool {} are added", mdmAddressesToAdd, uuid);
|
||||
applyMdmsChangeWaitTime(details);
|
||||
}
|
||||
} else {
|
||||
return new Ternary<>(false, getSDCDetails(details), "No MDM addresses provided");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -115,6 +115,145 @@ public class LibvirtGpuDefTest extends TestCase {
|
|||
assertTrue(gpuXml.contains("</hostdev>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGpuDef_withFullPciAddressDomainZero() {
|
||||
LibvirtGpuDef gpuDef = new LibvirtGpuDef();
|
||||
VgpuTypesInfo pciGpuInfo = new VgpuTypesInfo(
|
||||
GpuDevice.DeviceType.PCI,
|
||||
"passthrough",
|
||||
"passthrough",
|
||||
"0000:00:02.0",
|
||||
"10de",
|
||||
"NVIDIA Corporation",
|
||||
"1b38",
|
||||
"Tesla T4"
|
||||
);
|
||||
gpuDef.defGpu(pciGpuInfo);
|
||||
|
||||
String gpuXml = gpuDef.toString();
|
||||
|
||||
assertTrue(gpuXml.contains("<address domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGpuDef_withFullPciAddressNonZeroDomain() {
|
||||
LibvirtGpuDef gpuDef = new LibvirtGpuDef();
|
||||
VgpuTypesInfo pciGpuInfo = new VgpuTypesInfo(
|
||||
GpuDevice.DeviceType.PCI,
|
||||
"passthrough",
|
||||
"passthrough",
|
||||
"0001:65:00.0",
|
||||
"10de",
|
||||
"NVIDIA Corporation",
|
||||
"1b38",
|
||||
"Tesla T4"
|
||||
);
|
||||
gpuDef.defGpu(pciGpuInfo);
|
||||
|
||||
String gpuXml = gpuDef.toString();
|
||||
|
||||
assertTrue(gpuXml.contains("<address domain='0x0001' bus='0x65' slot='0x00' function='0x0'/>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGpuDef_withNvidiaStyleEightDigitDomain() {
|
||||
// nvidia-smi reports PCI addresses with an 8-digit domain (e.g. "00000001:af:00.1").
|
||||
// generatePciXml must normalize it to the canonical 4-digit form "0x0001".
|
||||
LibvirtGpuDef gpuDef = new LibvirtGpuDef();
|
||||
VgpuTypesInfo pciGpuInfo = new VgpuTypesInfo(
|
||||
GpuDevice.DeviceType.PCI,
|
||||
"passthrough",
|
||||
"passthrough",
|
||||
"00000001:af:00.1",
|
||||
"10de",
|
||||
"NVIDIA Corporation",
|
||||
"1b38",
|
||||
"Tesla T4"
|
||||
);
|
||||
gpuDef.defGpu(pciGpuInfo);
|
||||
|
||||
String gpuXml = gpuDef.toString();
|
||||
|
||||
assertTrue(gpuXml.contains("<address domain='0x0001' bus='0xaf' slot='0x00' function='0x1'/>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGpuDef_withFullPciAddressVfNonZeroDomain() {
|
||||
LibvirtGpuDef gpuDef = new LibvirtGpuDef();
|
||||
VgpuTypesInfo vfGpuInfo = new VgpuTypesInfo(
|
||||
GpuDevice.DeviceType.PCI,
|
||||
"VF-Profile",
|
||||
"VF-Profile",
|
||||
"0002:81:00.3",
|
||||
"10de",
|
||||
"NVIDIA Corporation",
|
||||
"1eb8",
|
||||
"Tesla T4"
|
||||
);
|
||||
gpuDef.defGpu(vfGpuInfo);
|
||||
|
||||
String gpuXml = gpuDef.toString();
|
||||
|
||||
// Non-passthrough NVIDIA VFs should be unmanaged
|
||||
assertTrue(gpuXml.contains("<hostdev mode='subsystem' type='pci' managed='no' display='off'>"));
|
||||
assertTrue(gpuXml.contains("<address domain='0x0002' bus='0x81' slot='0x00' function='0x3'/>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGpuDef_withLegacyShortBdfDefaultsDomainToZero() {
|
||||
// Backward compatibility: short BDF with no domain segment must still
|
||||
// produce a valid libvirt address with domain 0x0000.
|
||||
LibvirtGpuDef gpuDef = new LibvirtGpuDef();
|
||||
VgpuTypesInfo pciGpuInfo = new VgpuTypesInfo(
|
||||
GpuDevice.DeviceType.PCI,
|
||||
"passthrough",
|
||||
"passthrough",
|
||||
"af:00.0",
|
||||
"10de",
|
||||
"NVIDIA Corporation",
|
||||
"1b38",
|
||||
"Tesla T4"
|
||||
);
|
||||
gpuDef.defGpu(pciGpuInfo);
|
||||
|
||||
String gpuXml = gpuDef.toString();
|
||||
|
||||
assertTrue(gpuXml.contains("<address domain='0x0000' bus='0xaf' slot='0x00' function='0x0'/>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGpuDef_withInvalidBusAddressThrows() {
|
||||
String[] invalidAddresses = {
|
||||
"notahex:00.0", // non-hex bus
|
||||
"gg:00:02.0", // non-hex domain
|
||||
"00:02:03:04", // too many colon-separated parts
|
||||
"00", // missing slot/function
|
||||
"00:02", // missing function (no dot)
|
||||
"00:02.0.1", // extra dot in ss.f
|
||||
};
|
||||
for (String addr : invalidAddresses) {
|
||||
LibvirtGpuDef gpuDef = new LibvirtGpuDef();
|
||||
VgpuTypesInfo info = new VgpuTypesInfo(
|
||||
GpuDevice.DeviceType.PCI,
|
||||
"passthrough",
|
||||
"passthrough",
|
||||
addr,
|
||||
"10de",
|
||||
"NVIDIA Corporation",
|
||||
"1b38",
|
||||
"Tesla T4"
|
||||
);
|
||||
gpuDef.defGpu(info);
|
||||
try {
|
||||
String ignored = gpuDef.toString();
|
||||
fail("Expected IllegalArgumentException for address: " + addr + " but got: " + ignored);
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertTrue("Exception message should contain the bad address",
|
||||
e.getMessage().contains(addr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGpuDef_withNullDeviceType() {
|
||||
LibvirtGpuDef gpuDef = new LibvirtGpuDef();
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package com.cloud.hypervisor.kvm.resource.wrapper;
|
|||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.utils.script.Script;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.backup.BackupAnswer;
|
||||
|
|
@ -66,7 +67,10 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
when(command.getMountOptions()).thenReturn("rw");
|
||||
when(command.isVmExists()).thenReturn(null);
|
||||
when(command.getDiskType()).thenReturn("root");
|
||||
when(command.getRestoreVolumeSizes()).thenReturn(Arrays.asList(1024L));
|
||||
when(command.getWait()).thenReturn(60);
|
||||
PrimaryDataStoreTO primaryDataStore = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
when(primaryDataStore.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(primaryDataStore));
|
||||
when(command.getRestoreVolumePaths()).thenReturn(Arrays.asList("/var/lib/libvirt/images/volume-123"));
|
||||
when(command.getBackupFiles()).thenReturn(Arrays.asList("volume-123"));
|
||||
|
|
@ -109,6 +113,7 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
when(command.isVmExists()).thenReturn(true);
|
||||
when(command.getDiskType()).thenReturn("root");
|
||||
PrimaryDataStoreTO primaryDataStore = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
when(primaryDataStore.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(primaryDataStore));
|
||||
when(command.getRestoreVolumePaths()).thenReturn(Arrays.asList("/var/lib/libvirt/images/volume-123"));
|
||||
when(command.getBackupVolumesUUIDs()).thenReturn(Arrays.asList("volume-123"));
|
||||
|
|
@ -148,6 +153,7 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
when(command.isVmExists()).thenReturn(false);
|
||||
when(command.getDiskType()).thenReturn("root");
|
||||
PrimaryDataStoreTO primaryDataStore = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
when(primaryDataStore.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(primaryDataStore));
|
||||
when(command.getRestoreVolumePaths()).thenReturn(Arrays.asList("/var/lib/libvirt/images/volume-123"));
|
||||
when(command.getBackupFiles()).thenReturn(Arrays.asList("volume-123"));
|
||||
|
|
@ -185,7 +191,10 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
when(command.getMountOptions()).thenReturn("username=user,password=pass");
|
||||
when(command.isVmExists()).thenReturn(null);
|
||||
when(command.getDiskType()).thenReturn("root");
|
||||
when(command.getRestoreVolumeSizes()).thenReturn(Arrays.asList(1024L));
|
||||
when(command.getWait()).thenReturn(60);
|
||||
PrimaryDataStoreTO primaryDataStore = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
when(primaryDataStore.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(primaryDataStore));
|
||||
when(command.getRestoreVolumePaths()).thenReturn(Arrays.asList("/var/lib/libvirt/images/volume-123"));
|
||||
when(command.getBackupFiles()).thenReturn(Arrays.asList("volume-123"));
|
||||
|
|
@ -226,7 +235,10 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
lenient().when(command.getMountOptions()).thenReturn("rw");
|
||||
lenient().when(command.isVmExists()).thenReturn(null);
|
||||
lenient().when(command.getDiskType()).thenReturn("root");
|
||||
lenient().when(command.getRestoreVolumeSizes()).thenReturn(Arrays.asList(1024L));
|
||||
lenient().when(command.getWait()).thenReturn(60);
|
||||
PrimaryDataStoreTO primaryDataStore = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
lenient().when(primaryDataStore.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(primaryDataStore));
|
||||
lenient().when(command.getRestoreVolumePaths()).thenReturn(Arrays.asList("/var/lib/libvirt/images/volume-123"));
|
||||
when(command.getBackupFiles()).thenReturn(Arrays.asList("volume-123"));
|
||||
|
|
@ -262,7 +274,10 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
when(command.getMountOptions()).thenReturn("rw");
|
||||
when(command.isVmExists()).thenReturn(null);
|
||||
when(command.getDiskType()).thenReturn("root");
|
||||
when(command.getRestoreVolumeSizes()).thenReturn(Arrays.asList(1024L));
|
||||
when(command.getWait()).thenReturn(60);
|
||||
PrimaryDataStoreTO primaryDataStore = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
when(primaryDataStore.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(primaryDataStore));
|
||||
when(command.getRestoreVolumePaths()).thenReturn(Arrays.asList("/var/lib/libvirt/images/volume-123"));
|
||||
when(command.getBackupFiles()).thenReturn(Arrays.asList("volume-123"));
|
||||
|
|
@ -308,7 +323,10 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
when(command.getMountOptions()).thenReturn("rw");
|
||||
when(command.isVmExists()).thenReturn(null);
|
||||
when(command.getDiskType()).thenReturn("root");
|
||||
when(command.getRestoreVolumeSizes()).thenReturn(Arrays.asList(1024L));
|
||||
when(command.getWait()).thenReturn(60);
|
||||
PrimaryDataStoreTO primaryDataStore = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
when(primaryDataStore.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(primaryDataStore));
|
||||
when(command.getRestoreVolumePaths()).thenReturn(Arrays.asList("/var/lib/libvirt/images/volume-123"));
|
||||
when(command.getBackupFiles()).thenReturn(Arrays.asList("volume-123"));
|
||||
|
|
@ -356,7 +374,10 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
when(command.getMountOptions()).thenReturn("rw");
|
||||
when(command.isVmExists()).thenReturn(null);
|
||||
when(command.getDiskType()).thenReturn("root");
|
||||
when(command.getRestoreVolumeSizes()).thenReturn(Arrays.asList(1024L));
|
||||
when(command.getWait()).thenReturn(60);
|
||||
PrimaryDataStoreTO primaryDataStore = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
when(primaryDataStore.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(primaryDataStore));
|
||||
when(command.getRestoreVolumePaths()).thenReturn(Arrays.asList("/var/lib/libvirt/images/volume-123"));
|
||||
when(command.getBackupFiles()).thenReturn(Arrays.asList("volume-123"));
|
||||
|
|
@ -370,7 +391,15 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
|
||||
try (MockedStatic<Script> scriptMock = mockStatic(Script.class)) {
|
||||
scriptMock.when(() -> Script.runSimpleBashScriptForExitValue(anyString(), anyInt(), any(Boolean.class)))
|
||||
.thenReturn(0); // Mount success
|
||||
.thenAnswer(invocation -> {
|
||||
String command = invocation.getArgument(0);
|
||||
if (command.contains("mount")) {
|
||||
return 0; // mount success
|
||||
} else if (command.contains("rsync")) {
|
||||
return 1; // Rsync failure
|
||||
}
|
||||
return 0; // Other commands success
|
||||
});
|
||||
scriptMock.when(() -> Script.runSimpleBashScriptForExitValue(anyString()))
|
||||
.thenAnswer(invocation -> {
|
||||
String command = invocation.getArgument(0);
|
||||
|
|
@ -378,8 +407,6 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
return 0; // File exists
|
||||
} else if (command.contains("qemu-img check")) {
|
||||
return 0; // File is valid
|
||||
} else if (command.contains("rsync")) {
|
||||
return 1; // Rsync failure
|
||||
}
|
||||
return 0; // Other commands success
|
||||
});
|
||||
|
|
@ -406,7 +433,10 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
when(command.getMountOptions()).thenReturn("rw");
|
||||
when(command.isVmExists()).thenReturn(null);
|
||||
when(command.getDiskType()).thenReturn("root");
|
||||
when(command.getRestoreVolumeSizes()).thenReturn(Arrays.asList(1024L));
|
||||
when(command.getWait()).thenReturn(60);
|
||||
PrimaryDataStoreTO primaryDataStore = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
when(primaryDataStore.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(primaryDataStore));
|
||||
when(command.getRestoreVolumePaths()).thenReturn(Arrays.asList("/var/lib/libvirt/images/volume-123"));
|
||||
when(command.getBackupFiles()).thenReturn(Arrays.asList("volume-123"));
|
||||
|
|
@ -420,7 +450,15 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
|
||||
try (MockedStatic<Script> scriptMock = mockStatic(Script.class)) {
|
||||
scriptMock.when(() -> Script.runSimpleBashScriptForExitValue(anyString(), anyInt(), any(Boolean.class)))
|
||||
.thenReturn(0); // Mount success
|
||||
.thenAnswer(invocation -> {
|
||||
String command = invocation.getArgument(0);
|
||||
if (command.contains("mount")) {
|
||||
return 0; // Mount success
|
||||
} else if (command.contains("rsync")) {
|
||||
return 0; // Rsync success
|
||||
}
|
||||
return 0; // Other commands success
|
||||
});
|
||||
scriptMock.when(() -> Script.runSimpleBashScriptForExitValue(anyString()))
|
||||
.thenAnswer(invocation -> {
|
||||
String command = invocation.getArgument(0);
|
||||
|
|
@ -428,8 +466,6 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
return 0; // File exists
|
||||
} else if (command.contains("qemu-img check")) {
|
||||
return 0; // File is valid
|
||||
} else if (command.contains("rsync")) {
|
||||
return 0; // Rsync success
|
||||
} else if (command.contains("virsh attach-disk")) {
|
||||
return 1; // Attach failure
|
||||
}
|
||||
|
|
@ -460,7 +496,10 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
lenient().when(command.getMountOptions()).thenReturn("rw");
|
||||
lenient().when(command.isVmExists()).thenReturn(null);
|
||||
lenient().when(command.getDiskType()).thenReturn("root");
|
||||
lenient().when(command.getRestoreVolumeSizes()).thenReturn(Arrays.asList(1024L));
|
||||
lenient().when(command.getWait()).thenReturn(60);
|
||||
PrimaryDataStoreTO primaryDataStore = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
lenient().when(primaryDataStore.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(primaryDataStore));
|
||||
lenient().when(command.getRestoreVolumePaths()).thenReturn(Arrays.asList("/var/lib/libvirt/images/volume-123"));
|
||||
when(command.getBackupFiles()).thenReturn(Arrays.asList("volume-123"));
|
||||
|
|
@ -492,6 +531,8 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
when(command.getDiskType()).thenReturn("root");
|
||||
PrimaryDataStoreTO primaryDataStore1 = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
PrimaryDataStoreTO primaryDataStore2 = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
when(primaryDataStore1.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(primaryDataStore2.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(
|
||||
primaryDataStore1,
|
||||
primaryDataStore2
|
||||
|
|
@ -510,10 +551,10 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
filesMock.when(() -> Files.createTempDirectory(anyString())).thenReturn(tempPath);
|
||||
|
||||
try (MockedStatic<Script> scriptMock = mockStatic(Script.class)) {
|
||||
scriptMock.when(() -> Script.runSimpleBashScriptForExitValue(anyString(), anyInt(), any(Boolean.class)))
|
||||
.thenReturn(0); // Mount success
|
||||
scriptMock.when(() -> Script.runSimpleBashScriptForExitValue(anyString()))
|
||||
.thenReturn(0); // All other commands success
|
||||
.thenReturn(0); // All commands success
|
||||
scriptMock.when(() -> Script.runSimpleBashScriptForExitValue(anyString(), anyInt(), any(Boolean.class)))
|
||||
.thenReturn(0); // All commands success
|
||||
|
||||
filesMock.when(() -> Files.deleteIfExists(any(Path.class))).thenReturn(true);
|
||||
|
||||
|
|
|
|||
|
|
@ -485,6 +485,12 @@ public class MockAccountManager extends ManagerBase implements AccountManager {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long finalizeAccountId(Long accountId, String accountName, Long domainId, Long projectId) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkAccess(Account account, ServiceOffering so, DataCenter zone) throws PermissionDeniedException {
|
||||
// TODO Auto-generated method stub
|
||||
|
|
|
|||
|
|
@ -54,6 +54,13 @@ public class DeleteNsxNatRuleCommand extends NsxNetworkCommand {
|
|||
return protocol;
|
||||
}
|
||||
|
||||
public String getNetworkServiceName() {
|
||||
if (service != null) {
|
||||
return service.getName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
|
|
|||
|
|
@ -415,10 +415,10 @@ public class NsxResource implements ServerResource {
|
|||
|
||||
private NsxAnswer executeRequest(DeleteNsxNatRuleCommand cmd) {
|
||||
String ruleName = null;
|
||||
if (cmd.getService() == Network.Service.StaticNat) {
|
||||
if (Network.Service.StaticNat.getName().equals(cmd.getNetworkServiceName())) {
|
||||
ruleName = NsxControllerUtils.getStaticNatRuleName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(),
|
||||
cmd.getNetworkResourceId(), cmd.isResourceVpc());
|
||||
} else if (cmd.getService() == Network.Service.PortForwarding) {
|
||||
} else if (Network.Service.PortForwarding.getName().equals(cmd.getNetworkServiceName())) {
|
||||
ruleName = NsxControllerUtils.getPortForwardRuleName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(),
|
||||
cmd.getNetworkResourceId(), cmd.getRuleId(), cmd.isResourceVpc());
|
||||
}
|
||||
|
|
@ -456,7 +456,7 @@ public class NsxResource implements ServerResource {
|
|||
try {
|
||||
nsxApiClient.deleteNsxLbResources(tier1GatewayName, cmd.getLbId());
|
||||
} catch (Exception e) {
|
||||
logger.error(String.format("Failed to add NSX load balancer rule %s for network: %s", ruleName, cmd.getNetworkResourceName()));
|
||||
logger.error(String.format("Failed to delete NSX load balancer rule %s for network: %s", ruleName, cmd.getNetworkResourceName()));
|
||||
return new NsxAnswer(cmd, new CloudRuntimeException(e.getMessage()));
|
||||
}
|
||||
return new NsxAnswer(cmd, true, null);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
// under the License.
|
||||
package org.apache.cloudstack.resource;
|
||||
|
||||
import com.cloud.network.Network;
|
||||
import com.cloud.network.dao.NetworkVO;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.vmware.nsx.model.TransportZone;
|
||||
|
|
@ -61,6 +62,7 @@ import static org.mockito.ArgumentMatchers.anyLong;
|
|||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
|
|
@ -247,8 +249,12 @@ public class NsxResourceTest {
|
|||
@Test
|
||||
public void testDeleteNsxNatRule() {
|
||||
DeleteNsxNatRuleCommand cmd = new DeleteNsxNatRuleCommand(domainId, accountId, zoneId, 3L, "VPC01", true, 2L, 5L, "22", "tcp");
|
||||
Network.Service service = mock(Network.Service.class);
|
||||
when(service.getName()).thenReturn("PortForwarding");
|
||||
cmd.setService(service);
|
||||
NsxAnswer answer = (NsxAnswer) nsxResource.executeRequest(cmd);
|
||||
assertTrue(answer.getResult());
|
||||
verify(nsxApi).deleteNatRule(service, "22", "tcp", "VPC01", "D1-A2-Z1-V3", "D1-A2-Z1-V3-PF5");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@
|
|||
<module>storage/volume/default</module>
|
||||
<module>storage/volume/nexenta</module>
|
||||
<module>storage/volume/sample</module>
|
||||
<module>storage/volume/ontap</module>
|
||||
<module>storage/volume/solidfire</module>
|
||||
<module>storage/volume/scaleio</module>
|
||||
<module>storage/volume/linstor</module>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,123 @@
|
|||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
|
||||
# Apache CloudStack - NetApp ONTAP Storage Plugin
|
||||
|
||||
## Overview
|
||||
|
||||
The NetApp ONTAP Storage Plugin provides integration between Apache CloudStack and NetApp ONTAP storage systems. This plugin enables CloudStack to provision and manage primary storage on ONTAP clusters, supporting both NAS (NFS) and SAN (iSCSI) protocols.
|
||||
|
||||
## Features
|
||||
|
||||
- **Primary Storage Support**: Provision and manage primary storage pools on NetApp ONTAP
|
||||
- **Multiple Protocols**: Support for NFS 3.0 and iSCSI protocols
|
||||
- **Unified Storage**: Integration with traditional ONTAP unified storage architecture
|
||||
- **KVM Hypervisor Support**: Supports KVM hypervisor environments
|
||||
- **Managed Storage**: Operates as managed storage with full lifecycle management
|
||||
- **Flexible Scoping**: Support for Zone-wide and Cluster-scoped storage pools
|
||||
|
||||
## Architecture
|
||||
|
||||
### Component Structure
|
||||
|
||||
| Package | Description |
|
||||
|---------|-------------------------------------------------------|
|
||||
| `driver` | Primary datastore driver implementation |
|
||||
| `feign` | REST API clients and data models for ONTAP operations |
|
||||
| `lifecycle` | Storage pool lifecycle management |
|
||||
| `listener` | Host connection event handlers |
|
||||
| `provider` | Main provider and strategy factory |
|
||||
| `service` | ONTAP Storage strategy implementations (NAS/SAN) |
|
||||
| `utils` | Constants and helper utilities |
|
||||
|
||||
## Requirements
|
||||
|
||||
### ONTAP Requirements
|
||||
|
||||
- NetApp ONTAP 9.15.1 or higher
|
||||
- Storage Virtual Machine (SVM) configured with appropriate protocols enabled
|
||||
- Management LIF accessible from CloudStack management server
|
||||
- Data LIF(s) accessible from hypervisor hosts and are of IPv4 type
|
||||
- Aggregates assigned to the SVM with sufficient capacity
|
||||
|
||||
### CloudStack Requirements
|
||||
|
||||
- Apache CloudStack current version or higher
|
||||
- KVM hypervisor hosts
|
||||
- For iSCSI: Hosts must have iSCSI initiator configured with valid IQN
|
||||
- For NFS: Hosts must have NFS client packages installed
|
||||
|
||||
### Minimum Volume Size
|
||||
|
||||
ONTAP requires a minimum volume size of **1.56 GB** (1,677,721,600 bytes). The plugin will automatically adjust requested sizes below this threshold.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Storage Pool Creation Parameters
|
||||
|
||||
When creating an ONTAP primary storage pool, provide the following details in the URL field (semicolon-separated key=value pairs):
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|-----------|----------|-------------|
|
||||
| `username` | Yes | ONTAP cluster admin username |
|
||||
| `password` | Yes | ONTAP cluster admin password |
|
||||
| `svmName` | Yes | Storage Virtual Machine name |
|
||||
| `protocol` | Yes | Storage protocol (`NFS3` or `ISCSI`) |
|
||||
| `managementLIF` | Yes | ONTAP cluster management LIF IP address |
|
||||
|
||||
### Example URL Format
|
||||
|
||||
```
|
||||
username=admin;password=secretpass;svmName=svm1;protocol=ISCSI;managementLIF=192.168.1.100
|
||||
```
|
||||
|
||||
## Port Configuration
|
||||
|
||||
| Protocol | Default Port |
|
||||
|----------|--------------|
|
||||
| NFS | 2049 |
|
||||
| iSCSI | 3260 |
|
||||
| ONTAP Management API | 443 (HTTPS) |
|
||||
|
||||
## Limitations
|
||||
|
||||
- Supports only **KVM** hypervisor
|
||||
- Supports only **Unified ONTAP** storage (disaggregated not supported)
|
||||
- Supports only **NFS3** and **iSCSI** protocols
|
||||
- IPv6 type and FQDN LIFs are not supported
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Connection Failures**
|
||||
- Verify management LIF is reachable from CloudStack management server
|
||||
- Check firewall rules for port 443
|
||||
|
||||
2. **Protocol Errors**
|
||||
- Ensure the protocol (NFS/iSCSI) is enabled on the SVM
|
||||
- Verify Data LIFs are configured for the protocol
|
||||
|
||||
3. **Capacity Errors**
|
||||
- Check aggregate space availability
|
||||
- Ensure requested volume size meets minimum requirements (1.56 GB)
|
||||
|
||||
4. **Host Connection Issues**
|
||||
- For iSCSI: Verify host IQN is properly configured in host's storage URL
|
||||
- For NFS: Ensure NFS client is installed and running
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>cloud-plugin-storage-volume-ontap</artifactId>
|
||||
<name>Apache CloudStack Plugin - Storage Volume ONTAP Provider</name>
|
||||
<parent>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloudstack-plugins</artifactId>
|
||||
<version>4.23.0.0-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<properties>
|
||||
<spring-cloud.version>2021.0.7</spring-cloud.version>
|
||||
<openfeign.version>11.0</openfeign.version>
|
||||
<httpclient.version>4.5.14</httpclient.version>
|
||||
<swagger-annotations.version>1.6.2</swagger-annotations.version>
|
||||
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
|
||||
<maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
|
||||
<jackson-databind.version>2.13.4</jackson-databind.version>
|
||||
<assertj.version>3.24.2</assertj.version>
|
||||
<junit-jupiter.version>5.8.1</junit-jupiter.version>
|
||||
<mockito.version>3.12.4</mockito.version>
|
||||
<mockito-junit-jupiter.version>5.2.0</mockito-junit-jupiter.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>${spring-cloud.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-plugin-storage-volume-default</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.openfeign</groupId>
|
||||
<artifactId>feign-core</artifactId>
|
||||
<version>${openfeign.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.openfeign</groupId>
|
||||
<artifactId>feign-httpclient</artifactId>
|
||||
<version>${openfeign.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.openfeign</groupId>
|
||||
<artifactId>feign-jackson</artifactId>
|
||||
<version>${openfeign.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson-databind.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>${httpclient.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-engine-storage-volume</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>${swagger-annotations.version}</version>
|
||||
</dependency>
|
||||
<!-- JUnit 5 -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>${junit-jupiter.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Mockito -->
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
<version>${mockito-junit-jupiter.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Mockito Inline (for static method mocking) -->
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-inline</artifactId>
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>${assertj.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>central</id>
|
||||
<name>Maven Central</name>
|
||||
<url>https://repo.maven.apache.org/maven2</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${maven-compiler-plugin.version}</version>
|
||||
<configuration>
|
||||
<source>11</source>
|
||||
<target>11</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${maven-surefire-plugin.version}</version>
|
||||
<configuration>
|
||||
<skipTests>false</skipTests>
|
||||
<includes>
|
||||
<include>**/*Test.java</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>integration-test</phase>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.storage.driver;
|
||||
|
||||
import com.cloud.agent.api.to.DataStoreTO;
|
||||
import com.cloud.agent.api.to.DataTO;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.storage.StoragePool;
|
||||
import com.cloud.storage.Volume;
|
||||
import com.cloud.utils.Pair;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
|
||||
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
|
||||
import org.apache.cloudstack.storage.command.CommandResult;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class OntapPrimaryDatastoreDriver implements PrimaryDataStoreDriver {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(OntapPrimaryDatastoreDriver.class);
|
||||
|
||||
@Override
|
||||
public Map<String, String> getCapabilities() {
|
||||
logger.trace("OntapPrimaryDatastoreDriver: getCapabilities: Called");
|
||||
Map<String, String> mapCapabilities = new HashMap<>();
|
||||
mapCapabilities.put(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString(), Boolean.FALSE.toString());
|
||||
mapCapabilities.put(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_SNAPSHOT.toString(), Boolean.FALSE.toString());
|
||||
|
||||
return mapCapabilities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataTO getTO(DataObject data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataStoreTO getStoreTO(DataStore store) { return null; }
|
||||
|
||||
@Override
|
||||
public void createAsync(DataStore dataStore, DataObject dataObject, AsyncCompletionCallback<CreateCmdResult> callback) {
|
||||
throw new UnsupportedOperationException("Create operation is not supported for ONTAP primary storage.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAsync(DataStore store, DataObject data, AsyncCompletionCallback<CommandResult> callback) {
|
||||
throw new UnsupportedOperationException("Delete operation is not supported for ONTAP primary storage.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
|
||||
throw new UnsupportedOperationException("Copy operation is not supported for ONTAP primary storage.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
|
||||
throw new UnsupportedOperationException("Copy operation is not supported for ONTAP primary storage.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCopy(DataObject srcData, DataObject destData) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) {}
|
||||
|
||||
@Override
|
||||
public ChapInfo getChapInfo(DataObject dataObject) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean grantAccess(DataObject dataObject, Host host, DataStore dataStore) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokeAccess(DataObject dataObject, Host host, DataStore dataStore) {
|
||||
throw new UnsupportedOperationException("Revoke access operation is not supported for ONTAP primary storage.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDataObjectSizeIncludingHypervisorSnapshotReserve(DataObject dataObject, StoragePool storagePool) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getBytesRequiredForTemplate(TemplateInfo templateInfo, StoragePool storagePool) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getUsedBytes(StoragePool storagePool) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getUsedIops(StoragePool storagePool) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback) {}
|
||||
|
||||
@Override
|
||||
public void revertSnapshot(SnapshotInfo snapshotOnImageStore, SnapshotInfo snapshotOnPrimaryStore, AsyncCompletionCallback<CommandResult> callback) {}
|
||||
|
||||
@Override
|
||||
public void handleQualityOfServiceForVolumeMigration(VolumeInfo volumeInfo, QualityOfServiceState qualityOfServiceState) {}
|
||||
|
||||
@Override
|
||||
public boolean canProvideStorageStats() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<Long, Long> getStorageStats(StoragePool storagePool) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProvideVolumeStats() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<Long, Long> getVolumeStats(StoragePool storagePool, String volumeId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHostAccessStoragePool(Host host, StoragePool pool) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVmInfoNeeded() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void provideVmInfo(long vmId, long volumeId) {}
|
||||
|
||||
@Override
|
||||
public boolean isVmTagsNeeded(String tagKey) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void provideVmTags(long vmId, long volumeId, String tagValue) {}
|
||||
|
||||
@Override
|
||||
public boolean isStorageSupportHA(Storage.StoragePoolType type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void detachVolumeFromAllStorageNodes(Volume volume) {}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign;
|
||||
|
||||
import feign.Feign;
|
||||
|
||||
public class FeignClientFactory {
|
||||
|
||||
private final FeignConfiguration feignConfiguration;
|
||||
|
||||
public FeignClientFactory() {
|
||||
this.feignConfiguration = new FeignConfiguration();
|
||||
}
|
||||
|
||||
public FeignClientFactory(FeignConfiguration feignConfiguration) {
|
||||
this.feignConfiguration = feignConfiguration;
|
||||
}
|
||||
|
||||
public <T> T createClient(Class<T> clientClass, String baseURL) {
|
||||
return Feign.builder()
|
||||
.client(feignConfiguration.createClient())
|
||||
.encoder(feignConfiguration.createEncoder())
|
||||
.decoder(feignConfiguration.createDecoder())
|
||||
.retryer(feignConfiguration.createRetryer())
|
||||
.requestInterceptor(feignConfiguration.createRequestInterceptor())
|
||||
.target(clientClass, baseURL);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.Retryer;
|
||||
import feign.Client;
|
||||
import feign.httpclient.ApacheHttpClient;
|
||||
import feign.codec.Decoder;
|
||||
import feign.codec.Encoder;
|
||||
import feign.Response;
|
||||
import feign.codec.DecodeException;
|
||||
import feign.codec.EncodeException;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import org.apache.http.conn.ConnectionKeepAliveStrategy;
|
||||
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.TrustAllStrategy;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.ssl.SSLContexts;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class FeignConfiguration {
|
||||
private static final Logger logger = LogManager.getLogger(FeignConfiguration.class);
|
||||
|
||||
private final int retryMaxAttempt = 3;
|
||||
private final int retryMaxIntervalInSecs = 5;
|
||||
private final String ontapFeignMaxConnection = "80";
|
||||
private final String ontapFeignMaxConnectionPerRoute = "20";
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public FeignConfiguration() {
|
||||
this.objectMapper = new ObjectMapper();
|
||||
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
}
|
||||
|
||||
public Client createClient() {
|
||||
int maxConn;
|
||||
int maxConnPerRoute;
|
||||
try {
|
||||
maxConn = Integer.parseInt(this.ontapFeignMaxConnection);
|
||||
} catch (Exception e) {
|
||||
logger.error("ontapFeignClient: parse max connection failed, using default");
|
||||
maxConn = 20;
|
||||
}
|
||||
try {
|
||||
maxConnPerRoute = Integer.parseInt(this.ontapFeignMaxConnectionPerRoute);
|
||||
} catch (Exception e) {
|
||||
logger.error("ontapFeignClient: parse max connection per route failed, using default");
|
||||
maxConnPerRoute = 2;
|
||||
}
|
||||
logger.debug("ontapFeignClient: maxConn={}, maxConnPerRoute={}", maxConn, maxConnPerRoute);
|
||||
ConnectionKeepAliveStrategy keepAliveStrategy = (response, context) -> 0;
|
||||
CloseableHttpClient httpClient = HttpClientBuilder.create()
|
||||
.setMaxConnTotal(maxConn)
|
||||
.setMaxConnPerRoute(maxConnPerRoute)
|
||||
.setKeepAliveStrategy(keepAliveStrategy)
|
||||
.setSSLSocketFactory(getSSLSocketFactory())
|
||||
.setConnectionTimeToLive(60, TimeUnit.SECONDS)
|
||||
.build();
|
||||
return new ApacheHttpClient(httpClient);
|
||||
}
|
||||
|
||||
private SSLConnectionSocketFactory getSSLSocketFactory() {
|
||||
try {
|
||||
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustAllStrategy()).build();
|
||||
return new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public RequestInterceptor createRequestInterceptor() {
|
||||
return template -> {
|
||||
logger.info("Feign Request URL: {}", template.url());
|
||||
logger.info("HTTP Method: {}", template.method());
|
||||
logger.trace("Headers: {}", template.headers());
|
||||
if (template.body() != null) {
|
||||
logger.info("Body: {}", new String(template.body(), StandardCharsets.UTF_8));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public Retryer createRetryer() {
|
||||
return new Retryer.Default(1000L, retryMaxIntervalInSecs * 1000L, retryMaxAttempt);
|
||||
}
|
||||
|
||||
public Encoder createEncoder() {
|
||||
return new Encoder() {
|
||||
@Override
|
||||
public void encode(Object object, Type bodyType, feign.RequestTemplate template) throws EncodeException {
|
||||
if (object == null) {
|
||||
template.body(null, StandardCharsets.UTF_8);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
byte[] jsonBytes = objectMapper.writeValueAsBytes(object);
|
||||
template.body(jsonBytes, StandardCharsets.UTF_8);
|
||||
template.header("Content-Type", "application/json");
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new EncodeException("Error encoding object to JSON", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public Decoder createDecoder() {
|
||||
return new Decoder() {
|
||||
@Override
|
||||
public Object decode(Response response, Type type) throws IOException, DecodeException {
|
||||
if (response.body() == null) {
|
||||
logger.debug("Response body is null, returning null");
|
||||
return null;
|
||||
}
|
||||
String json = null;
|
||||
try (InputStream bodyStream = response.body().asInputStream()) {
|
||||
json = new String(bodyStream.readAllBytes(), StandardCharsets.UTF_8);
|
||||
logger.debug("Decoding JSON response: {}", json);
|
||||
return objectMapper.readValue(json, objectMapper.getTypeFactory().constructType(type));
|
||||
} catch (IOException e) {
|
||||
logger.error("IOException during decoding. Status: {}, Raw body: {}", response.status(), json, e);
|
||||
throw new DecodeException(response.status(), "Error decoding JSON response", response.request(), e);
|
||||
} catch (Exception e) {
|
||||
logger.error("Unexpected error during decoding. Status: {}, Type: {}, Raw body: {}", response.status(), type, json, e);
|
||||
throw new DecodeException(response.status(), "Unexpected error during decoding", response.request(), e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.client;
|
||||
|
||||
import org.apache.cloudstack.storage.feign.model.Aggregate;
|
||||
import org.apache.cloudstack.storage.feign.model.response.OntapResponse;
|
||||
import feign.Headers;
|
||||
import feign.Param;
|
||||
import feign.RequestLine;
|
||||
|
||||
public interface AggregateFeignClient {
|
||||
|
||||
@RequestLine("GET /api/storage/aggregates")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
OntapResponse<Aggregate> getAggregateResponse(@Param("authHeader") String authHeader);
|
||||
|
||||
@RequestLine("GET /api/storage/aggregates/{uuid}")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
Aggregate getAggregateByUUID(@Param("authHeader") String authHeader, @Param("uuid") String uuid);
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.client;
|
||||
|
||||
import org.apache.cloudstack.storage.feign.model.Cluster;
|
||||
import feign.Headers;
|
||||
import feign.Param;
|
||||
import feign.RequestLine;
|
||||
|
||||
public interface ClusterFeignClient {
|
||||
|
||||
@RequestLine("GET /api/cluster")
|
||||
@Headers({"Authorization: {authHeader}", "return_records: {returnRecords}"})
|
||||
Cluster getCluster(@Param("authHeader") String authHeader, @Param("returnRecords") boolean returnRecords);
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.storage.feign.client;
|
||||
|
||||
import org.apache.cloudstack.storage.feign.model.Job;
|
||||
import feign.Headers;
|
||||
import feign.Param;
|
||||
import feign.RequestLine;
|
||||
|
||||
public interface JobFeignClient {
|
||||
|
||||
@RequestLine("GET /api/cluster/jobs/{uuid}")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
Job getJobByUUID(@Param("authHeader") String authHeader, @Param("uuid") String uuid);
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.client;
|
||||
|
||||
import feign.QueryMap;
|
||||
import org.apache.cloudstack.storage.feign.model.ExportPolicy;
|
||||
import org.apache.cloudstack.storage.feign.model.FileInfo;
|
||||
import org.apache.cloudstack.storage.feign.model.response.OntapResponse;
|
||||
import feign.Headers;
|
||||
import feign.Param;
|
||||
import feign.RequestLine;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface NASFeignClient {
|
||||
|
||||
// File Operations
|
||||
@RequestLine("GET /api/storage/volumes/{volumeUuid}/files/{path}")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
OntapResponse<FileInfo> getFileResponse(@Param("authHeader") String authHeader,
|
||||
@Param("volumeUuid") String volumeUUID,
|
||||
@Param("path") String filePath);
|
||||
|
||||
@RequestLine("DELETE /api/storage/volumes/{volumeUuid}/files/{path}")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
void deleteFile(@Param("authHeader") String authHeader,
|
||||
@Param("volumeUuid") String volumeUUID,
|
||||
@Param("path") String filePath);
|
||||
|
||||
@RequestLine("PATCH /api/storage/volumes/{volumeUuid}/files/{path}")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
void updateFile(@Param("authHeader") String authHeader,
|
||||
@Param("volumeUuid") String volumeUUID,
|
||||
@Param("path") String filePath,
|
||||
FileInfo fileInfo);
|
||||
|
||||
@RequestLine("POST /api/storage/volumes/{volumeUuid}/files/{path}")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
void createFile(@Param("authHeader") String authHeader,
|
||||
@Param("volumeUuid") String volumeUUID,
|
||||
@Param("path") String filePath,
|
||||
FileInfo file);
|
||||
|
||||
// Export Policy Operations
|
||||
@RequestLine("POST /api/protocols/nfs/export-policies")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
void createExportPolicy(@Param("authHeader") String authHeader,
|
||||
ExportPolicy exportPolicy);
|
||||
|
||||
@RequestLine("GET /api/protocols/nfs/export-policies")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
OntapResponse<ExportPolicy> getExportPolicyResponse(@Param("authHeader") String authHeader, @QueryMap Map<String, Object> queryMap);
|
||||
|
||||
@RequestLine("GET /api/protocols/nfs/export-policies/{id}")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
ExportPolicy getExportPolicyById(@Param("authHeader") String authHeader,
|
||||
@Param("id") String id);
|
||||
|
||||
@RequestLine("DELETE /api/protocols/nfs/export-policies/{id}")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
void deleteExportPolicyById(@Param("authHeader") String authHeader,
|
||||
@Param("id") String id);
|
||||
|
||||
@RequestLine("PATCH /api/protocols/nfs/export-policies/{id}")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
OntapResponse<ExportPolicy> updateExportPolicy(@Param("authHeader") String authHeader,
|
||||
@Param("id") String id,
|
||||
ExportPolicy request);
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.storage.feign.client;
|
||||
|
||||
import feign.Headers;
|
||||
import feign.Param;
|
||||
import feign.QueryMap;
|
||||
import feign.RequestLine;
|
||||
import org.apache.cloudstack.storage.feign.model.IpInterface;
|
||||
import org.apache.cloudstack.storage.feign.model.response.OntapResponse;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface NetworkFeignClient {
|
||||
@RequestLine("GET /api/network/ip/interfaces")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
OntapResponse<IpInterface> getNetworkIpInterfaces(@Param("authHeader") String authHeader, @QueryMap Map<String, Object> queryParams);
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.storage.feign.client;
|
||||
|
||||
import feign.QueryMap;
|
||||
import org.apache.cloudstack.storage.feign.model.Igroup;
|
||||
import org.apache.cloudstack.storage.feign.model.IscsiService;
|
||||
import org.apache.cloudstack.storage.feign.model.Lun;
|
||||
import org.apache.cloudstack.storage.feign.model.LunMap;
|
||||
import org.apache.cloudstack.storage.feign.model.response.OntapResponse;
|
||||
import feign.Headers;
|
||||
import feign.Param;
|
||||
import feign.RequestLine;
|
||||
import java.util.Map;
|
||||
|
||||
public interface SANFeignClient {
|
||||
// iSCSI Service APIs
|
||||
@RequestLine("GET /api/protocols/san/iscsi/services")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
OntapResponse<IscsiService> getIscsiServices(@Param("authHeader") String authHeader, @QueryMap Map<String, Object> queryMap);
|
||||
|
||||
// LUN Operation APIs
|
||||
@RequestLine("POST /api/storage/luns?return_records={returnRecords}")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
OntapResponse<Lun> createLun(@Param("authHeader") String authHeader, @Param("returnRecords") boolean returnRecords, Lun lun);
|
||||
|
||||
@RequestLine("GET /api/storage/luns")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
OntapResponse<Lun> getLunResponse(@Param("authHeader") String authHeader, @QueryMap Map<String, Object> queryMap);
|
||||
|
||||
@RequestLine("GET /{uuid}")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
Lun getLunByUUID(@Param("authHeader") String authHeader, @Param("uuid") String uuid);
|
||||
|
||||
@RequestLine("PATCH /{uuid}")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
void updateLun(@Param("authHeader") String authHeader, @Param("uuid") String uuid, Lun lun);
|
||||
|
||||
@RequestLine("DELETE /api/storage/luns/{uuid}")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
void deleteLun(@Param("authHeader") String authHeader, @Param("uuid") String uuid, @QueryMap Map<String, Object> queryMap);
|
||||
|
||||
// iGroup Operation APIs
|
||||
@RequestLine("POST /api/protocols/san/igroups?return_records={returnRecords}")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
OntapResponse<Igroup> createIgroup(@Param("authHeader") String authHeader, @Param("returnRecords") boolean returnRecords, Igroup igroupRequest);
|
||||
|
||||
@RequestLine("GET /api/protocols/san/igroups")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
OntapResponse<Igroup> getIgroupResponse(@Param("authHeader") String authHeader, @QueryMap Map<String, Object> queryMap);
|
||||
|
||||
@RequestLine("GET /{uuid}")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
Igroup getIgroupByUUID(@Param("authHeader") String authHeader, @Param("uuid") String uuid);
|
||||
|
||||
@RequestLine("DELETE /api/protocols/san/igroups/{uuid}")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
void deleteIgroup(@Param("authHeader") String authHeader, @Param("uuid") String uuid);
|
||||
|
||||
// LUN Maps Operation APIs
|
||||
@RequestLine("POST /api/protocols/san/lun-maps")
|
||||
@Headers({"Authorization: {authHeader}", "return_records: {returnRecords}"})
|
||||
OntapResponse<LunMap> createLunMap(@Param("authHeader") String authHeader, @Param("returnRecords") boolean returnRecords, LunMap lunMap);
|
||||
|
||||
|
||||
@RequestLine("GET /api/protocols/san/lun-maps")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
OntapResponse<LunMap> getLunMapResponse(@Param("authHeader") String authHeader, @QueryMap Map<String, Object> queryMap);
|
||||
|
||||
@RequestLine("DELETE /api/protocols/san/lun-maps/{lunUuid}/{igroupUuid}")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
void deleteLunMap(@Param("authHeader") String authHeader,
|
||||
@Param("lunUuid") String lunUUID,
|
||||
@Param("igroupUuid") String igroupUUID);
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.client;
|
||||
|
||||
import feign.QueryMap;
|
||||
import org.apache.cloudstack.storage.feign.model.Svm;
|
||||
import org.apache.cloudstack.storage.feign.model.response.OntapResponse;
|
||||
import feign.Headers;
|
||||
import feign.Param;
|
||||
import feign.RequestLine;
|
||||
import java.util.Map;
|
||||
|
||||
public interface SvmFeignClient {
|
||||
|
||||
// SVM Operation APIs
|
||||
@RequestLine("GET /api/svm/svms")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
OntapResponse<Svm> getSvmResponse(@QueryMap Map<String, Object> queryMap, @Param("authHeader") String authHeader);
|
||||
|
||||
@RequestLine("GET /api/svm/svms/{uuid}")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
Svm getSvmByUUID(@Param("authHeader") String authHeader, @Param("uuid") String uuid);
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.storage.feign.client;
|
||||
|
||||
import feign.QueryMap;
|
||||
import org.apache.cloudstack.storage.feign.model.Volume;
|
||||
import org.apache.cloudstack.storage.feign.model.response.JobResponse;
|
||||
import feign.Headers;
|
||||
import feign.Param;
|
||||
import feign.RequestLine;
|
||||
import org.apache.cloudstack.storage.feign.model.response.OntapResponse;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface VolumeFeignClient {
|
||||
|
||||
@RequestLine("DELETE /api/storage/volumes/{uuid}")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
JobResponse deleteVolume(@Param("authHeader") String authHeader, @Param("uuid") String uuid);
|
||||
|
||||
@RequestLine("POST /api/storage/volumes")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
JobResponse createVolumeWithJob(@Param("authHeader") String authHeader, Volume volumeRequest);
|
||||
|
||||
@RequestLine("GET /api/storage/volumes")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
OntapResponse<Volume> getAllVolumes(@Param("authHeader") String authHeader, @QueryMap Map<String, Object> queryParams);
|
||||
|
||||
@RequestLine("GET /api/storage/volumes/{uuid}")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
Volume getVolumeByUUID(@Param("authHeader") String authHeader, @Param("uuid") String uuid);
|
||||
|
||||
@RequestLine("GET /api/storage/volumes")
|
||||
@Headers({"Authorization: {authHeader}"})
|
||||
OntapResponse<Volume> getVolume(@Param("authHeader") String authHeader, @QueryMap Map<String, Object> queryMap);
|
||||
|
||||
@RequestLine("PATCH /api/storage/volumes/{uuid}")
|
||||
@Headers({ "Authorization: {authHeader}"})
|
||||
JobResponse updateVolumeRebalancing(@Param("authHeader") String authHeader, @Param("uuid") String uuid, Volume volumeRequest);
|
||||
}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Aggregate {
|
||||
// Replace previous enum with case-insensitive mapping
|
||||
public enum StateEnum {
|
||||
ONLINE("online");
|
||||
private final String value;
|
||||
|
||||
StateEnum(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@JsonValue
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public static StateEnum fromValue(String text) {
|
||||
for (StateEnum b : StateEnum.values()) {
|
||||
if (String.valueOf(b.value).equals(text)) {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonProperty("name")
|
||||
private String name = null;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getName(), getUuid());
|
||||
}
|
||||
|
||||
@JsonProperty("uuid")
|
||||
private String uuid = null;
|
||||
|
||||
@JsonProperty("state")
|
||||
private StateEnum state = null;
|
||||
|
||||
@JsonProperty("space")
|
||||
private AggregateSpace space = null;
|
||||
|
||||
|
||||
public Aggregate name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Aggregate uuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public StateEnum getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public AggregateSpace getSpace() {
|
||||
return space;
|
||||
}
|
||||
|
||||
public Double getAvailableBlockStorageSpace() {
|
||||
if (space != null && space.blockStorage != null) {
|
||||
return space.blockStorage.available;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(java.lang.Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Aggregate diskAggregates = (Aggregate) o;
|
||||
return Objects.equals(this.name, diskAggregates.name) &&
|
||||
Objects.equals(this.uuid, diskAggregates.uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given object to string with each line indented by 4 spaces
|
||||
* (except the first line).
|
||||
*/
|
||||
private String toIndentedString(java.lang.Object o) {
|
||||
if (o == null) {
|
||||
return "null";
|
||||
}
|
||||
return o.toString().replace("\n", "\n ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DiskAggregates [name=" + name + ", uuid=" + uuid + "]";
|
||||
}
|
||||
|
||||
public static class AggregateSpace {
|
||||
@JsonProperty("block_storage")
|
||||
private AggregateSpaceBlockStorage blockStorage = null;
|
||||
}
|
||||
|
||||
public static class AggregateSpaceBlockStorage {
|
||||
@JsonProperty("available")
|
||||
private Double available = null;
|
||||
@JsonProperty("size")
|
||||
private Double size = null;
|
||||
@JsonProperty("used")
|
||||
private Double used = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class AntiRansomware {
|
||||
@JsonProperty("state")
|
||||
private String state;
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Complete cluster information
|
||||
*/
|
||||
@SuppressWarnings("checkstyle:RegexpSingleline")
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Cluster {
|
||||
|
||||
@JsonProperty("name")
|
||||
private String name = null;
|
||||
@JsonProperty("uuid")
|
||||
private String uuid = null;
|
||||
@JsonProperty("version")
|
||||
private Version version = null;
|
||||
@JsonProperty("health")
|
||||
private String health = null;
|
||||
|
||||
@JsonProperty("san_optimized")
|
||||
private Boolean sanOptimized = null;
|
||||
|
||||
@JsonProperty("disaggregated")
|
||||
private Boolean disaggregated = null;
|
||||
|
||||
|
||||
public String getHealth() {
|
||||
return health;
|
||||
}
|
||||
|
||||
public void setHealth(String health) {
|
||||
this.health = health;
|
||||
}
|
||||
|
||||
public Cluster name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public Cluster version(Version version) {
|
||||
this.version = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Version getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Version version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Boolean getSanOptimized() {
|
||||
return sanOptimized;
|
||||
}
|
||||
|
||||
public void setSanOptimized(Boolean sanOptimized) {
|
||||
this.sanOptimized = sanOptimized;
|
||||
}
|
||||
|
||||
public Boolean getDisaggregated() {
|
||||
return disaggregated;
|
||||
}
|
||||
public void setDisaggregated(Boolean disaggregated) {
|
||||
this.disaggregated = disaggregated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getName(), getUuid());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(java.lang.Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Cluster cluster = (Cluster) o;
|
||||
return Objects.equals(this.name, cluster.name) &&
|
||||
Objects.equals(this.uuid, cluster.uuid);
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Cluster{" +
|
||||
"name='" + name + '\'' +
|
||||
", uuid='" + uuid + '\'' +
|
||||
", version=" + version +
|
||||
", sanOptimized=" + sanOptimized +
|
||||
", disaggregated=" + disaggregated +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class ExportPolicy {
|
||||
|
||||
@JsonProperty("id")
|
||||
private BigInteger id = null;
|
||||
@JsonProperty("name")
|
||||
private String name = null;
|
||||
@JsonProperty("rules")
|
||||
private List<ExportRule> rules = null;
|
||||
@JsonProperty("svm")
|
||||
private Svm svm = null;
|
||||
|
||||
public BigInteger getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public ExportPolicy name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public ExportPolicy rules(List<ExportRule> rules) {
|
||||
this.rules = rules;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<ExportRule> getRules() {
|
||||
return rules;
|
||||
}
|
||||
|
||||
public void setRules(List<ExportRule> rules) {
|
||||
this.rules = rules;
|
||||
}
|
||||
|
||||
public ExportPolicy svm(Svm svm) {
|
||||
this.svm = svm;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Svm getSvm() {
|
||||
return svm;
|
||||
}
|
||||
|
||||
public void setSvm(Svm svm) {
|
||||
this.svm = svm;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ExportPolicy exportPolicy = (ExportPolicy) o;
|
||||
return Objects.equals(this.id, exportPolicy.id) &&
|
||||
Objects.equals(this.name, exportPolicy.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash( id, name);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("class ExportPolicy {\n");
|
||||
sb.append(" id: ").append(toIndentedString(id)).append("\n");
|
||||
sb.append(" name: ").append(toIndentedString(name)).append("\n");
|
||||
sb.append(" rules: ").append(toIndentedString(rules)).append("\n");
|
||||
sb.append(" svm: ").append(toIndentedString(svm)).append("\n");
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
private String toIndentedString(Object o) {
|
||||
if (o == null) {
|
||||
return "null";
|
||||
}
|
||||
return o.toString().replace("\n", "\n ");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ExportRule
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class ExportRule {
|
||||
@JsonProperty("anonymous_user")
|
||||
private String anonymousUser ;
|
||||
|
||||
@JsonProperty("clients")
|
||||
private List<ExportClient> clients = null;
|
||||
|
||||
@JsonProperty("index")
|
||||
private Integer index = null;
|
||||
|
||||
public enum ProtocolsEnum {
|
||||
ANY("any"),
|
||||
|
||||
NFS("nfs"),
|
||||
|
||||
NFS3("nfs3"),
|
||||
|
||||
NFS4("nfs4");
|
||||
|
||||
private String value;
|
||||
|
||||
ProtocolsEnum(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
public static ProtocolsEnum fromValue(String text) {
|
||||
for (ProtocolsEnum b : ProtocolsEnum.values()) {
|
||||
if (String.valueOf(b.value).equals(text)) {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonProperty("protocols")
|
||||
private List<ProtocolsEnum> protocols = null;
|
||||
|
||||
@JsonProperty("ro_rule")
|
||||
private List<String> roRule = null;
|
||||
|
||||
@JsonProperty("rw_rule")
|
||||
private List<String> rwRule = null;
|
||||
|
||||
@JsonProperty("superuser")
|
||||
private List<String> superuser = null;
|
||||
|
||||
|
||||
public ExportRule anonymousUser(String anonymousUser) {
|
||||
this.anonymousUser = anonymousUser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getAnonymousUser() {
|
||||
return anonymousUser;
|
||||
}
|
||||
|
||||
public void setAnonymousUser(String anonymousUser) {
|
||||
this.anonymousUser = anonymousUser;
|
||||
}
|
||||
|
||||
public ExportRule clients(List<ExportClient> clients) {
|
||||
this.clients = clients;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<ExportClient> getClients() {
|
||||
return clients;
|
||||
}
|
||||
|
||||
public void setClients(List<ExportClient> clients) {
|
||||
this.clients = clients;
|
||||
}
|
||||
|
||||
public Integer getIndex() {
|
||||
return index;
|
||||
}
|
||||
public void setIndex(Integer index)
|
||||
{
|
||||
this.index=index;
|
||||
}
|
||||
|
||||
public ExportRule protocols(List<ProtocolsEnum> protocols) {
|
||||
this.protocols = protocols;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<ProtocolsEnum> getProtocols() {
|
||||
return protocols;
|
||||
}
|
||||
|
||||
public void setProtocols(List<ProtocolsEnum> protocols) {
|
||||
this.protocols = protocols;
|
||||
}
|
||||
|
||||
public static class ExportClient {
|
||||
@JsonProperty("match")
|
||||
private String match = null;
|
||||
|
||||
public ExportClient match (String match) {
|
||||
this.match = match;
|
||||
return this;
|
||||
}
|
||||
public String getMatch () {
|
||||
return match;
|
||||
}
|
||||
|
||||
public void setMatch (String match) {
|
||||
this.match = match;
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getRwRule() {
|
||||
return rwRule;
|
||||
}
|
||||
|
||||
public void setRwRule(List<String> rwRule) {
|
||||
this.rwRule = rwRule;
|
||||
}
|
||||
|
||||
public List<String> getRoRule() {
|
||||
return roRule;
|
||||
}
|
||||
|
||||
public void setRoRule(List<String> roRule) {
|
||||
this.roRule = roRule;
|
||||
}
|
||||
|
||||
public List<String> getSuperuser() {
|
||||
return superuser;
|
||||
}
|
||||
|
||||
public void setSuperuser(List<String> superuser) {
|
||||
this.superuser = superuser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("class ExportRule {\n");
|
||||
|
||||
sb.append(" anonymousUser: ").append(toIndentedString(anonymousUser)).append("\n");
|
||||
sb.append(" clients: ").append(toIndentedString(clients)).append("\n");
|
||||
sb.append(" index: ").append(toIndentedString(index)).append("\n");
|
||||
sb.append(" protocols: ").append(toIndentedString(protocols)).append("\n");
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
private String toIndentedString(Object o) {
|
||||
if (o == null) {
|
||||
return "null";
|
||||
}
|
||||
return o.toString().replace("\n", "\n ");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,297 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Information about a single file.
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class FileInfo {
|
||||
@JsonProperty("bytes_used")
|
||||
private Long bytesUsed = null;
|
||||
@JsonProperty("creation_time")
|
||||
private OffsetDateTime creationTime = null;
|
||||
@JsonProperty("fill_enabled")
|
||||
private Boolean fillEnabled = null;
|
||||
@JsonProperty("is_empty")
|
||||
private Boolean isEmpty = null;
|
||||
@JsonProperty("is_snapshot")
|
||||
private Boolean isSnapshot = null;
|
||||
@JsonProperty("is_vm_aligned")
|
||||
private Boolean isVmAligned = null;
|
||||
@JsonProperty("modified_time")
|
||||
private OffsetDateTime modifiedTime = null;
|
||||
@JsonProperty("name")
|
||||
private String name = null;
|
||||
@JsonProperty("overwrite_enabled")
|
||||
private Boolean overwriteEnabled = null;
|
||||
@JsonProperty("path")
|
||||
private String path = null;
|
||||
@JsonProperty("size")
|
||||
private Long size = null;
|
||||
@JsonProperty("target")
|
||||
private String target = null;
|
||||
|
||||
/**
|
||||
* Type of the file.
|
||||
*/
|
||||
public enum TypeEnum {
|
||||
FILE("file"),
|
||||
DIRECTORY("directory");
|
||||
|
||||
private String value;
|
||||
|
||||
TypeEnum(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@JsonValue
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public static TypeEnum fromValue(String value) {
|
||||
for (TypeEnum b : TypeEnum.values()) {
|
||||
if (b.value.equals(value)) {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonProperty("type")
|
||||
private TypeEnum type = null;
|
||||
|
||||
@JsonProperty("unique_bytes")
|
||||
private Long uniqueBytes = null;
|
||||
|
||||
@JsonProperty("unix_permissions")
|
||||
private Integer unixPermissions = null;
|
||||
|
||||
/**
|
||||
* The actual number of bytes used on disk by this file. If byte_offset and length parameters are specified, this will return the bytes used by the file within the given range.
|
||||
* @return bytesUsed
|
||||
**/
|
||||
public Long getBytesUsed() {
|
||||
return bytesUsed;
|
||||
}
|
||||
|
||||
public OffsetDateTime getCreationTime() {
|
||||
return creationTime;
|
||||
}
|
||||
|
||||
public FileInfo fillEnabled(Boolean fillEnabled) {
|
||||
this.fillEnabled = fillEnabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Boolean isFillEnabled() {
|
||||
return fillEnabled;
|
||||
}
|
||||
|
||||
public void setFillEnabled(Boolean fillEnabled) {
|
||||
this.fillEnabled = fillEnabled;
|
||||
}
|
||||
|
||||
|
||||
public Boolean isIsEmpty() {
|
||||
return isEmpty;
|
||||
}
|
||||
|
||||
public void setIsEmpty(Boolean isEmpty) {
|
||||
this.isEmpty = isEmpty;
|
||||
}
|
||||
|
||||
public Boolean isIsSnapshot() {
|
||||
return isSnapshot;
|
||||
}
|
||||
|
||||
public void setIsSnapshot(Boolean isSnapshot) {
|
||||
this.isSnapshot = isSnapshot;
|
||||
}
|
||||
|
||||
|
||||
public Boolean isIsVmAligned() {
|
||||
return isVmAligned;
|
||||
}
|
||||
|
||||
|
||||
public OffsetDateTime getModifiedTime() {
|
||||
return modifiedTime;
|
||||
}
|
||||
|
||||
public FileInfo name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public FileInfo overwriteEnabled(Boolean overwriteEnabled) {
|
||||
this.overwriteEnabled = overwriteEnabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Boolean isOverwriteEnabled() {
|
||||
return overwriteEnabled;
|
||||
}
|
||||
|
||||
public void setOverwriteEnabled(Boolean overwriteEnabled) {
|
||||
this.overwriteEnabled = overwriteEnabled;
|
||||
}
|
||||
|
||||
public FileInfo path(String path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
public Long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(Long size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public FileInfo target(String target) {
|
||||
this.target = target;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public void setTarget(String target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public FileInfo type(TypeEnum type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TypeEnum getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(TypeEnum type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Long getUniqueBytes() {
|
||||
return uniqueBytes;
|
||||
}
|
||||
|
||||
public FileInfo unixPermissions(Integer unixPermissions) {
|
||||
this.unixPermissions = unixPermissions;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Integer getUnixPermissions() {
|
||||
return unixPermissions;
|
||||
}
|
||||
|
||||
public void setUnixPermissions(Integer unixPermissions) {
|
||||
this.unixPermissions = unixPermissions;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
FileInfo fileInfo = (FileInfo) o;
|
||||
return Objects.equals(this.name, fileInfo.name) &&
|
||||
Objects.equals(this.path, fileInfo.path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, path);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("class FileInfo {\n");
|
||||
sb.append(" bytesUsed: ").append(toIndentedString(bytesUsed)).append("\n");
|
||||
sb.append(" creationTime: ").append(toIndentedString(creationTime)).append("\n");
|
||||
sb.append(" fillEnabled: ").append(toIndentedString(fillEnabled)).append("\n");
|
||||
sb.append(" isEmpty: ").append(toIndentedString(isEmpty)).append("\n");
|
||||
sb.append(" isSnapshot: ").append(toIndentedString(isSnapshot)).append("\n");
|
||||
sb.append(" isVmAligned: ").append(toIndentedString(isVmAligned)).append("\n");
|
||||
sb.append(" modifiedTime: ").append(toIndentedString(modifiedTime)).append("\n");
|
||||
sb.append(" name: ").append(toIndentedString(name)).append("\n");
|
||||
sb.append(" overwriteEnabled: ").append(toIndentedString(overwriteEnabled)).append("\n");
|
||||
sb.append(" path: ").append(toIndentedString(path)).append("\n");
|
||||
sb.append(" size: ").append(toIndentedString(size)).append("\n");
|
||||
sb.append(" target: ").append(toIndentedString(target)).append("\n");
|
||||
sb.append(" type: ").append(toIndentedString(type)).append("\n");
|
||||
sb.append(" uniqueBytes: ").append(toIndentedString(uniqueBytes)).append("\n");
|
||||
sb.append(" unixPermissions: ").append(toIndentedString(unixPermissions)).append("\n");
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given object to string with each line indented by 4 spaces
|
||||
* (except the first line).
|
||||
*/
|
||||
private String toIndentedString(Object o) {
|
||||
if (o == null) {
|
||||
return "null";
|
||||
}
|
||||
return o.toString().replace("\n", "\n ");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,257 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Igroup {
|
||||
@JsonProperty("delete_on_unmap")
|
||||
private Boolean deleteOnUnmap = null;
|
||||
@JsonProperty("initiators")
|
||||
private List<Initiator> initiators = null;
|
||||
@JsonProperty("lun_maps")
|
||||
private List<LunMap> lunMaps = null;
|
||||
@JsonProperty("os_type")
|
||||
private OsTypeEnum osType = null;
|
||||
|
||||
@JsonProperty("parent_igroups")
|
||||
private List<Igroup> parentIgroups = null;
|
||||
|
||||
@JsonProperty("igroups")
|
||||
private List<Igroup> igroups = null;
|
||||
|
||||
@JsonProperty("name")
|
||||
private String name = null;
|
||||
|
||||
@JsonProperty("protocol")
|
||||
private ProtocolEnum protocol = null;
|
||||
@JsonProperty("svm")
|
||||
private Svm svm = null;
|
||||
@JsonProperty("uuid")
|
||||
private String uuid = null;
|
||||
|
||||
public enum OsTypeEnum {
|
||||
HyperV("hyper_v"),
|
||||
|
||||
Linux("linux"),
|
||||
|
||||
VMware("vmware"),
|
||||
|
||||
Windows("windows"),
|
||||
|
||||
Xen("xen");
|
||||
|
||||
private String value;
|
||||
|
||||
OsTypeEnum(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
public static OsTypeEnum fromValue(String text) {
|
||||
for (OsTypeEnum b : OsTypeEnum.values()) {
|
||||
if (String.valueOf(b.value).equals(text)) {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public List<Igroup> getParentIgroups() {
|
||||
return parentIgroups;
|
||||
}
|
||||
|
||||
public void setParentIgroups(List<Igroup> parentIgroups) {
|
||||
this.parentIgroups = parentIgroups;
|
||||
}
|
||||
|
||||
public Igroup igroups(List<Igroup> igroups) {
|
||||
this.igroups = igroups;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<Igroup> getIgroups() {
|
||||
return igroups;
|
||||
}
|
||||
|
||||
public void setIgroups(List<Igroup> igroups) {
|
||||
this.igroups = igroups;
|
||||
}
|
||||
|
||||
public enum ProtocolEnum {
|
||||
iscsi("iscsi"),
|
||||
|
||||
mixed("mixed");
|
||||
|
||||
private String value;
|
||||
|
||||
ProtocolEnum(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
public static ProtocolEnum fromValue(String text) {
|
||||
for (ProtocolEnum b : ProtocolEnum.values()) {
|
||||
if (String.valueOf(b.value).equals(text)) {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public Igroup deleteOnUnmap(Boolean deleteOnUnmap) {
|
||||
this.deleteOnUnmap = deleteOnUnmap;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Boolean isDeleteOnUnmap() {
|
||||
return deleteOnUnmap;
|
||||
}
|
||||
|
||||
public void setDeleteOnUnmap(Boolean deleteOnUnmap) {
|
||||
this.deleteOnUnmap = deleteOnUnmap;
|
||||
}
|
||||
|
||||
public Igroup initiators(List<Initiator> initiators) {
|
||||
this.initiators = initiators;
|
||||
return this;
|
||||
}
|
||||
public List<Initiator> getInitiators() {
|
||||
return initiators;
|
||||
}
|
||||
|
||||
public void setInitiators(List<Initiator> initiators) {
|
||||
this.initiators = initiators;
|
||||
}
|
||||
|
||||
public Igroup lunMaps(List<LunMap> lunMaps) {
|
||||
this.lunMaps = lunMaps;
|
||||
return this;
|
||||
}
|
||||
public List<LunMap> getLunMaps() {
|
||||
return lunMaps;
|
||||
}
|
||||
|
||||
public void setLunMaps(List<LunMap> lunMaps) {
|
||||
this.lunMaps = lunMaps;
|
||||
}
|
||||
|
||||
public Igroup name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Igroup osType(OsTypeEnum osType) {
|
||||
this.osType = osType;
|
||||
return this;
|
||||
}
|
||||
public OsTypeEnum getOsType() {
|
||||
return osType;
|
||||
}
|
||||
|
||||
public void setOsType(OsTypeEnum osType) {
|
||||
this.osType = osType;
|
||||
}
|
||||
|
||||
public Igroup protocol(ProtocolEnum protocol) {
|
||||
this.protocol = protocol;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProtocolEnum getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public void setProtocol(ProtocolEnum protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public Igroup svm(Svm svm) {
|
||||
this.svm = svm;
|
||||
return this;
|
||||
}
|
||||
public Svm getSvm() {
|
||||
return svm;
|
||||
}
|
||||
|
||||
public void setSvm(Svm svm) {
|
||||
this.svm = svm;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Igroup other = (Igroup) obj;
|
||||
return Objects.equals(name, other.name) && Objects.equals(uuid, other.uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Igroup [deleteOnUnmap=" + deleteOnUnmap + ", initiators=" + initiators + ", lunMaps=" + lunMaps
|
||||
+ ", name=" + name + ", replication=" + ", osType=" + osType + ", parentIgroups="
|
||||
+ parentIgroups + ", igroups=" + igroups + ", protocol=" + protocol + ", svm=" + svm + ", uuid=" + uuid
|
||||
+ ", portset=" + "]";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Initiator {
|
||||
@JsonProperty("name")
|
||||
private String name = null;
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class IpInterface {
|
||||
@JsonProperty("uuid")
|
||||
private String uuid;
|
||||
|
||||
@JsonProperty("name")
|
||||
private String name;
|
||||
|
||||
@JsonProperty("ip")
|
||||
private IpInfo ip;
|
||||
|
||||
@JsonProperty("svm")
|
||||
private Svm svm;
|
||||
|
||||
@JsonProperty("services")
|
||||
private List<String> services;
|
||||
|
||||
// Getters and setters
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public IpInfo getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public void setIp(IpInfo ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
public Svm getSvm() {
|
||||
return svm;
|
||||
}
|
||||
|
||||
public void setSvm(Svm svm) {
|
||||
this.svm = svm;
|
||||
}
|
||||
|
||||
public List<String> getServices() {
|
||||
return services;
|
||||
}
|
||||
|
||||
public void setServices(List<String> services) {
|
||||
this.services = services;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
IpInterface that = (IpInterface) o;
|
||||
return Objects.equals(uuid, that.uuid) &&
|
||||
Objects.equals(name, that.name) &&
|
||||
Objects.equals(ip, that.ip) &&
|
||||
Objects.equals(svm, that.svm) &&
|
||||
Objects.equals(services, that.services);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(uuid, name, ip, svm, services);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IpInterface{" +
|
||||
"uuid='" + uuid + '\'' +
|
||||
", name='" + name + '\'' +
|
||||
", ip=" + ip +
|
||||
", svm=" + svm +
|
||||
", services=" + services +
|
||||
'}';
|
||||
}
|
||||
|
||||
// Nested class for IP information
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public static class IpInfo {
|
||||
@JsonProperty("address")
|
||||
private String address;
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
IpInfo ipInfo = (IpInfo) o;
|
||||
return Objects.equals(address, ipInfo.address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IpInfo{" +
|
||||
"address='" + address + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
|
||||
/**
|
||||
* An iSCSI service defines the properties of the iSCSI target for an SVM.
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class IscsiService {
|
||||
@JsonProperty("enabled")
|
||||
private Boolean enabled = null;
|
||||
|
||||
@JsonProperty("svm")
|
||||
private Svm svm = null;
|
||||
|
||||
@JsonProperty("target")
|
||||
private IscsiServiceTarget target = null;
|
||||
|
||||
public Boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public Svm getSvm() {
|
||||
return svm;
|
||||
}
|
||||
|
||||
public void setSvm(Svm svm) {
|
||||
this.svm = svm;
|
||||
}
|
||||
|
||||
public IscsiServiceTarget getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public void setTarget(IscsiServiceTarget target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IscsiService{" +
|
||||
"enabled=" + enabled +
|
||||
", svm=" + svm +
|
||||
", target=" + target +
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* iSCSI target information
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public static class IscsiServiceTarget {
|
||||
@JsonProperty("alias")
|
||||
private String alias = null;
|
||||
|
||||
@JsonProperty("name")
|
||||
private String name = null;
|
||||
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
public void setAlias(String alias) {
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IscsiServiceTarget{" +
|
||||
"alias='" + alias + '\'' +
|
||||
", name='" + name + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* @author Administrator
|
||||
*
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
public class Job {
|
||||
|
||||
@JsonProperty("uuid")
|
||||
String uuid;
|
||||
@JsonProperty("description")
|
||||
String description;
|
||||
@JsonProperty("state")
|
||||
String state;
|
||||
@JsonProperty("message")
|
||||
String message;
|
||||
@JsonProperty("code")
|
||||
String code;
|
||||
@JsonProperty("_links")
|
||||
private Links links;
|
||||
|
||||
@JsonProperty("error")
|
||||
private JobError error;
|
||||
public JobError getError () { return error; }
|
||||
public void setError (JobError error) { this.error = error; }
|
||||
public Links getLinks() { return links; }
|
||||
public void setLinks(Links links) { this.links = links; }
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JobDTO [uuid=" + uuid + ", description=" + description + ", state=" + state + ", message="
|
||||
+ message + ", code=" + code + "]";
|
||||
}
|
||||
|
||||
public static class Links {
|
||||
@JsonProperty("self")
|
||||
private Self self;
|
||||
public Self getSelf() { return self; }
|
||||
public void setSelf(Self self) { this.self = self; }
|
||||
}
|
||||
|
||||
public static class Self {
|
||||
@JsonProperty("href")
|
||||
private String href;
|
||||
public String getHref() { return href; }
|
||||
public void setHref(String href) { this.href = href; }
|
||||
}
|
||||
|
||||
public static class JobError {
|
||||
@JsonProperty("message")
|
||||
String errorMesssage;
|
||||
@JsonProperty("code")
|
||||
String code;
|
||||
public String getErrorMesssage () { return errorMesssage; }
|
||||
public void setErrorMesssage (String errorMesssage) { this.errorMesssage = errorMesssage; }
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JobError [errorMesssage=" + errorMesssage + ", code=" + code + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,341 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A LUN is the logical representation of storage in a storage area network (SAN).<br/> In ONTAP, a LUN is located within a volume. Optionally, it can be located within a qtree in a volume.<br/> A LUN can be created to a specified size using thin or thick provisioning. A LUN can then be renamed, resized, cloned, and moved to a different volume. LUNs support the assignment of a quality of service (QoS) policy for performance management or a QoS policy can be assigned to the volume containing the LUN. See the LUN object model to learn more about each of the properties supported by the LUN REST API.<br/> A LUN must be mapped to an initiator group to grant access to the initiator group's initiators (client hosts). Initiators can then access the LUN and perform I/O over a Fibre Channel (FC) fabric using the Fibre Channel Protocol or a TCP/IP network using iSCSI.
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Lun {
|
||||
|
||||
@JsonProperty("auto_delete")
|
||||
private Boolean autoDelete = null;
|
||||
|
||||
/**
|
||||
* The class of LUN.<br/> Optional in POST.
|
||||
*/
|
||||
public enum PropertyClassEnum {
|
||||
REGULAR("regular");
|
||||
|
||||
private String value;
|
||||
|
||||
PropertyClassEnum(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@JsonValue
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public static PropertyClassEnum fromValue(String value) {
|
||||
for (PropertyClassEnum b : PropertyClassEnum.values()) {
|
||||
if (b.value.equals(value)) {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonProperty("class")
|
||||
private PropertyClassEnum propertyClass = null;
|
||||
|
||||
@JsonProperty("enabled")
|
||||
private Boolean enabled = null;
|
||||
|
||||
@JsonProperty("lun_maps")
|
||||
private List<LunMap> lunMaps = null;
|
||||
|
||||
@JsonProperty("name")
|
||||
private String name = null;
|
||||
|
||||
@JsonProperty("clone")
|
||||
private Clone clone = null;
|
||||
|
||||
/**
|
||||
* The operating system type of the LUN.<br/> Required in POST when creating a LUN that is not a clone of another. Disallowed in POST when creating a LUN clone.
|
||||
*/
|
||||
public enum OsTypeEnum {
|
||||
HYPER_V("hyper_v"),
|
||||
|
||||
LINUX("linux"),
|
||||
|
||||
VMWARE("vmware"),
|
||||
|
||||
WINDOWS("windows"),
|
||||
|
||||
XEN("xen");
|
||||
|
||||
private String value;
|
||||
|
||||
OsTypeEnum(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@JsonValue
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public static OsTypeEnum fromValue(String value) {
|
||||
for (OsTypeEnum b : OsTypeEnum.values()) {
|
||||
if (b.value.equals(value)) {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonProperty("os_type")
|
||||
private OsTypeEnum osType = null;
|
||||
|
||||
@JsonProperty("serial_number")
|
||||
private String serialNumber = null;
|
||||
|
||||
@JsonProperty("space")
|
||||
private LunSpace space = null;
|
||||
|
||||
@JsonProperty("svm")
|
||||
private Svm svm = null;
|
||||
|
||||
@JsonProperty("uuid")
|
||||
private String uuid = null;
|
||||
|
||||
public Lun autoDelete(Boolean autoDelete) {
|
||||
this.autoDelete = autoDelete;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Boolean isAutoDelete() {
|
||||
return autoDelete;
|
||||
}
|
||||
|
||||
public void setAutoDelete(Boolean autoDelete) {
|
||||
this.autoDelete = autoDelete;
|
||||
}
|
||||
|
||||
public Lun propertyClass(PropertyClassEnum propertyClass) {
|
||||
this.propertyClass = propertyClass;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PropertyClassEnum getPropertyClass() {
|
||||
return propertyClass;
|
||||
}
|
||||
|
||||
public void setPropertyClass(PropertyClassEnum propertyClass) {
|
||||
this.propertyClass = propertyClass;
|
||||
}
|
||||
|
||||
public Lun enabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public List<LunMap> getLunMaps() {
|
||||
return lunMaps;
|
||||
}
|
||||
|
||||
public void setLunMaps(List<LunMap> lunMaps) {
|
||||
this.lunMaps = lunMaps;
|
||||
}
|
||||
|
||||
public Lun name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Lun osType(OsTypeEnum osType) {
|
||||
this.osType = osType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OsTypeEnum getOsType() {
|
||||
return osType;
|
||||
}
|
||||
|
||||
public void setOsType(OsTypeEnum osType) {
|
||||
this.osType = osType;
|
||||
}
|
||||
|
||||
public String getSerialNumber() {
|
||||
return serialNumber;
|
||||
}
|
||||
|
||||
public Lun space(LunSpace space) {
|
||||
this.space = space;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LunSpace getSpace() {
|
||||
return space;
|
||||
}
|
||||
|
||||
public void setSpace(LunSpace space) {
|
||||
this.space = space;
|
||||
}
|
||||
|
||||
public Lun svm(Svm svm) {
|
||||
this.svm = svm;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Svm getSvm() {
|
||||
return svm;
|
||||
}
|
||||
|
||||
public void setSvm(Svm svm) {
|
||||
this.svm = svm;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public Clone getClone() {
|
||||
return clone;
|
||||
}
|
||||
|
||||
public void setClone(Clone clone) {
|
||||
this.clone = clone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Lun lun = (Lun) o;
|
||||
return Objects.equals(this.name, lun.name) && Objects.equals(this.uuid, lun.uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, uuid);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("class Lun {\n");
|
||||
sb.append(" autoDelete: ").append(toIndentedString(autoDelete)).append("\n");
|
||||
sb.append(" propertyClass: ").append(toIndentedString(propertyClass)).append("\n");
|
||||
sb.append(" enabled: ").append(toIndentedString(enabled)).append("\n");
|
||||
sb.append(" lunMaps: ").append(toIndentedString(lunMaps)).append("\n");
|
||||
sb.append(" name: ").append(toIndentedString(name)).append("\n");
|
||||
sb.append(" osType: ").append(toIndentedString(osType)).append("\n");
|
||||
sb.append(" serialNumber: ").append(toIndentedString(serialNumber)).append("\n");
|
||||
sb.append(" space: ").append(toIndentedString(space)).append("\n");
|
||||
sb.append(" svm: ").append(toIndentedString(svm)).append("\n");
|
||||
sb.append(" uuid: ").append(toIndentedString(uuid)).append("\n");
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given object to string with each line indented by 4 spaces
|
||||
* (except the first line).
|
||||
*/
|
||||
private String toIndentedString(Object o) {
|
||||
if (o == null) {
|
||||
return "null";
|
||||
}
|
||||
return o.toString().replace("\n", "\n ");
|
||||
}
|
||||
|
||||
|
||||
public static class Clone {
|
||||
@JsonProperty("source")
|
||||
private Source source = null;
|
||||
public Source getSource() {
|
||||
return source;
|
||||
}
|
||||
public void setSource(Source source) {
|
||||
this.source = source;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Source {
|
||||
@JsonProperty("name")
|
||||
private String name = null;
|
||||
@JsonProperty("uuid")
|
||||
private String uuid = null;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class LunMap {
|
||||
@JsonProperty("igroup")
|
||||
private Igroup igroup = null;
|
||||
@JsonProperty("logical_unit_number")
|
||||
private Integer logicalUnitNumber = null;
|
||||
@JsonProperty("lun")
|
||||
private Lun lun = null;
|
||||
@JsonProperty("svm")
|
||||
@SerializedName("svm")
|
||||
private Svm svm = null;
|
||||
|
||||
public LunMap igroup (Igroup igroup) {
|
||||
this.igroup = igroup;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Igroup getIgroup () {
|
||||
return igroup;
|
||||
}
|
||||
|
||||
public void setIgroup (Igroup igroup) {
|
||||
this.igroup = igroup;
|
||||
}
|
||||
|
||||
public LunMap logicalUnitNumber (Integer logicalUnitNumber) {
|
||||
this.logicalUnitNumber = logicalUnitNumber;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Integer getLogicalUnitNumber () {
|
||||
return logicalUnitNumber;
|
||||
}
|
||||
|
||||
public void setLogicalUnitNumber (Integer logicalUnitNumber) {
|
||||
this.logicalUnitNumber = logicalUnitNumber;
|
||||
}
|
||||
|
||||
public LunMap lun (Lun lun) {
|
||||
this.lun = lun;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Lun getLun () {
|
||||
return lun;
|
||||
}
|
||||
|
||||
public void setLun (Lun lun) {
|
||||
this.lun = lun;
|
||||
}
|
||||
|
||||
public LunMap svm (Svm svm) {
|
||||
this.svm = svm;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Svm getSvm () {
|
||||
return svm;
|
||||
}
|
||||
|
||||
public void setSvm (Svm svm) {
|
||||
this.svm = svm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString () {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("class LunMap {\n");
|
||||
sb.append(" igroup: ").append(toIndentedString(igroup)).append("\n");
|
||||
sb.append(" logicalUnitNumber: ").append(toIndentedString(logicalUnitNumber)).append("\n");
|
||||
sb.append(" lun: ").append(toIndentedString(lun)).append("\n");
|
||||
sb.append(" svm: ").append(toIndentedString(svm)).append("\n");
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String toIndentedString (Object o) {
|
||||
if (o == null) {
|
||||
return "null";
|
||||
}
|
||||
return o.toString().replace("\n", "\n ");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* The storage space related properties of the LUN.
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class LunSpace {
|
||||
|
||||
@JsonProperty("scsi_thin_provisioning_support_enabled")
|
||||
private Boolean scsiThinProvisioningSupportEnabled = null;
|
||||
|
||||
@JsonProperty("size")
|
||||
private Long size = null;
|
||||
|
||||
@JsonProperty("used")
|
||||
private Long used = null;
|
||||
@JsonProperty("physical_used")
|
||||
private Long physicalUsed = null;
|
||||
|
||||
public LunSpace scsiThinProvisioningSupportEnabled(Boolean scsiThinProvisioningSupportEnabled) {
|
||||
this.scsiThinProvisioningSupportEnabled = scsiThinProvisioningSupportEnabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Boolean isScsiThinProvisioningSupportEnabled() {
|
||||
return scsiThinProvisioningSupportEnabled;
|
||||
}
|
||||
|
||||
public void setScsiThinProvisioningSupportEnabled(Boolean scsiThinProvisioningSupportEnabled) {
|
||||
this.scsiThinProvisioningSupportEnabled = scsiThinProvisioningSupportEnabled;
|
||||
}
|
||||
|
||||
public LunSpace size(Long size) {
|
||||
this.size = size;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(Long size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public Long getUsed() {
|
||||
return used;
|
||||
}
|
||||
|
||||
public Long getPhysicalUsed() {
|
||||
return physicalUsed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("class LunSpace {\n");
|
||||
sb.append(" scsiThinProvisioningSupportEnabled: ").append(toIndentedString(scsiThinProvisioningSupportEnabled)).append("\n");
|
||||
sb.append(" size: ").append(toIndentedString(size)).append("\n");
|
||||
sb.append(" used: ").append(toIndentedString(used)).append("\n");
|
||||
sb.append(" physicalUsed: ").append(toIndentedString(physicalUsed)).append("\n");
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String toIndentedString(Object o) {
|
||||
if (o == null) {
|
||||
return "null";
|
||||
}
|
||||
return o.toString().replace("\n", "\n ");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Nas {
|
||||
@JsonProperty("path")
|
||||
private String path;
|
||||
|
||||
@JsonProperty("export_policy")
|
||||
private ExportPolicy exportPolicy;
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public ExportPolicy getExportPolicy() {
|
||||
return exportPolicy;
|
||||
}
|
||||
|
||||
public void setExportPolicy(ExportPolicy exportPolicy) {
|
||||
this.exportPolicy = exportPolicy;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import org.apache.cloudstack.storage.service.model.ProtocolType;
|
||||
|
||||
public class OntapStorage {
|
||||
private final String username;
|
||||
private final String password;
|
||||
private final String managementLIF;
|
||||
private final String svmName;
|
||||
private final Long size;
|
||||
private final ProtocolType protocolType;
|
||||
private final Boolean isDisaggregated;
|
||||
|
||||
public OntapStorage(String username, String password, String managementLIF, String svmName, Long size, ProtocolType protocolType, Boolean isDisaggregated) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.managementLIF = managementLIF;
|
||||
this.svmName = svmName;
|
||||
this.size = size;
|
||||
this.protocolType = protocolType;
|
||||
this.isDisaggregated = isDisaggregated;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getManagementLIF() {
|
||||
return managementLIF;
|
||||
}
|
||||
|
||||
public String getSvmName() {
|
||||
return svmName;
|
||||
}
|
||||
|
||||
public Long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public ProtocolType getProtocol() {
|
||||
return protocolType;
|
||||
}
|
||||
|
||||
public Boolean getIsDisaggregated() {
|
||||
return isDisaggregated;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Policy {
|
||||
private int minThroughputIops;
|
||||
private int minThroughputMbps;
|
||||
private int maxThroughputIops;
|
||||
private int maxThroughputMbps;
|
||||
private String uuid;
|
||||
private String name;
|
||||
public int getMinThroughputIops() { return minThroughputIops; }
|
||||
public void setMinThroughputIops(int minThroughputIops) { this.minThroughputIops = minThroughputIops; }
|
||||
public int getMinThroughputMbps() { return minThroughputMbps; }
|
||||
public void setMinThroughputMbps(int minThroughputMbps) { this.minThroughputMbps = minThroughputMbps; }
|
||||
public int getMaxThroughputIops() { return maxThroughputIops; }
|
||||
public void setMaxThroughputIops(int maxThroughputIops) { this.maxThroughputIops = maxThroughputIops; }
|
||||
public int getMaxThroughputMbps() { return maxThroughputMbps; }
|
||||
public void setMaxThroughputMbps(int maxThroughputMbps) { this.maxThroughputMbps = maxThroughputMbps; }
|
||||
public String getUuid() { return uuid; }
|
||||
public void setUuid(String uuid) { this.uuid = uuid; }
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Policy policy = (Policy) o;
|
||||
return Objects.equals(getUuid(), policy.getUuid());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(getUuid());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Qos {
|
||||
@JsonProperty("policy")
|
||||
private Policy policy;
|
||||
|
||||
public Policy getPolicy() {
|
||||
return policy;
|
||||
}
|
||||
|
||||
public void setPolicy(Policy policy) {
|
||||
this.policy = policy;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Svm {
|
||||
@JsonProperty("uuid")
|
||||
private String uuid = null;
|
||||
|
||||
@JsonProperty("name")
|
||||
private String name = null;
|
||||
|
||||
@JsonProperty("iscsi.enabled")
|
||||
private Boolean iscsiEnabled = null;
|
||||
|
||||
@JsonProperty("fcp.enabled")
|
||||
private Boolean fcpEnabled = null;
|
||||
|
||||
@JsonProperty("nfs.enabled")
|
||||
private Boolean nfsEnabled = null;
|
||||
|
||||
@JsonProperty("aggregates")
|
||||
private List<Aggregate> aggregates = null;
|
||||
|
||||
@JsonProperty("aggregates_delegated")
|
||||
private Boolean aggregatesDelegated = null;
|
||||
|
||||
@JsonProperty("state")
|
||||
private String state = null;
|
||||
|
||||
@JsonIgnore
|
||||
private Links links = null;
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Boolean getIscsiEnabled() {
|
||||
return iscsiEnabled;
|
||||
}
|
||||
|
||||
public void setIscsiEnabled(Boolean iscsiEnabled) {
|
||||
this.iscsiEnabled = iscsiEnabled;
|
||||
}
|
||||
|
||||
public Boolean getFcpEnabled() {
|
||||
return fcpEnabled;
|
||||
}
|
||||
|
||||
public void setFcpEnabled(Boolean fcpEnabled) {
|
||||
this.fcpEnabled = fcpEnabled;
|
||||
}
|
||||
|
||||
public Boolean getNfsEnabled() {
|
||||
return nfsEnabled;
|
||||
}
|
||||
|
||||
public void setNfsEnabled(Boolean nfsEnabled) {
|
||||
this.nfsEnabled = nfsEnabled;
|
||||
}
|
||||
|
||||
public List<Aggregate> getAggregates() {
|
||||
return aggregates;
|
||||
}
|
||||
|
||||
public void setAggregates(List<Aggregate> aggregates) {
|
||||
this.aggregates = aggregates;
|
||||
}
|
||||
|
||||
public Boolean getAggregatesDelegated() {
|
||||
return aggregatesDelegated;
|
||||
}
|
||||
|
||||
public void setAggregatesDelegated(Boolean aggregatesDelegated) {
|
||||
this.aggregatesDelegated = aggregatesDelegated;
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public Links getLinks() {
|
||||
return links;
|
||||
}
|
||||
|
||||
public void setLinks(Links links) {
|
||||
this.links = links;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Svm svm = (Svm) o;
|
||||
return Objects.equals(getUuid(), svm.getUuid());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(getUuid());
|
||||
}
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public static class Links { }
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* This returns the cluster version information. When the cluster has more than one node, the cluster version is equivalent to the lowest of generation, major, and minor versions on all nodes.
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Version {
|
||||
@JsonProperty("full")
|
||||
private String full = null;
|
||||
|
||||
@JsonProperty("generation")
|
||||
private Integer generation = null;
|
||||
|
||||
@JsonProperty("major")
|
||||
private Integer major = null;
|
||||
|
||||
@JsonProperty("minor")
|
||||
private Integer minor = null;
|
||||
|
||||
public String getFull() {
|
||||
return full;
|
||||
}
|
||||
|
||||
public Integer getGeneration() {
|
||||
return generation;
|
||||
}
|
||||
|
||||
public Integer getMajor() {
|
||||
return major;
|
||||
}
|
||||
|
||||
public Integer getMinor() {
|
||||
return minor;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Version clusterVersion = (Version) o;
|
||||
return Objects.equals(this.full, clusterVersion.full) &&
|
||||
Objects.equals(this.generation, clusterVersion.generation) &&
|
||||
Objects.equals(this.major, clusterVersion.major) &&
|
||||
Objects.equals(this.minor, clusterVersion.minor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(full, generation, major, minor);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("class ClusterVersion {\n");
|
||||
sb.append(" full: ").append(toIndentedString(full)).append("\n");
|
||||
sb.append(" generation: ").append(toIndentedString(generation)).append("\n");
|
||||
sb.append(" major: ").append(toIndentedString(major)).append("\n");
|
||||
sb.append(" minor: ").append(toIndentedString(minor)).append("\n");
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given object to string with each line indented by 4 spaces
|
||||
* (except the first line).
|
||||
*/
|
||||
private String toIndentedString(Object o) {
|
||||
if (o == null) {
|
||||
return "null";
|
||||
}
|
||||
return o.toString().replace("\n", "\n ");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Volume {
|
||||
@JsonProperty("uuid")
|
||||
private String uuid;
|
||||
|
||||
@JsonProperty("name")
|
||||
private String name;
|
||||
|
||||
@JsonProperty("state")
|
||||
private String state;
|
||||
|
||||
@JsonProperty("nas")
|
||||
private Nas nas;
|
||||
|
||||
@JsonProperty("svm")
|
||||
private Svm svm;
|
||||
|
||||
@JsonProperty("qos")
|
||||
private Qos qos;
|
||||
|
||||
@JsonProperty("space")
|
||||
private VolumeSpace space;
|
||||
|
||||
@JsonProperty("anti_ransomware")
|
||||
private AntiRansomware antiRansomware;
|
||||
|
||||
@JsonProperty("aggregates")
|
||||
private List<Aggregate> aggregates = null;
|
||||
|
||||
@JsonProperty("size")
|
||||
private Long size = null;
|
||||
|
||||
// Getters and setters
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public Nas getNas() {
|
||||
return nas;
|
||||
}
|
||||
|
||||
public void setNas(Nas nas) {
|
||||
this.nas = nas;
|
||||
}
|
||||
|
||||
public Svm getSvm() {
|
||||
return svm;
|
||||
}
|
||||
|
||||
public void setSvm(Svm svm) {
|
||||
this.svm = svm;
|
||||
}
|
||||
|
||||
public Qos getQos() {
|
||||
return qos;
|
||||
}
|
||||
|
||||
public void setQos(Qos qos) {
|
||||
this.qos = qos;
|
||||
}
|
||||
|
||||
public VolumeSpace getSpace() {
|
||||
return space;
|
||||
}
|
||||
|
||||
public void setSpace(VolumeSpace space) {
|
||||
this.space = space;
|
||||
}
|
||||
|
||||
public AntiRansomware getAntiRansomware() {
|
||||
return antiRansomware;
|
||||
}
|
||||
|
||||
public void setAntiRansomware(AntiRansomware antiRansomware) {
|
||||
this.antiRansomware = antiRansomware;
|
||||
}
|
||||
|
||||
public List<Aggregate> getAggregates () { return aggregates; }
|
||||
|
||||
public void setAggregates (List<Aggregate> aggregates) { this.aggregates = aggregates; }
|
||||
|
||||
public Long getSize () { return size; }
|
||||
|
||||
public void setSize (Long size) { this.size = size; }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Volume volume = (Volume) o;
|
||||
return Objects.equals(uuid, volume.uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(uuid);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class VolumeQosPolicy {
|
||||
@JsonProperty("max_throughput_iops")
|
||||
private Integer maxThroughputIops = null;
|
||||
|
||||
@JsonProperty("max_throughput_mbps")
|
||||
private Integer maxThroughputMbps = null;
|
||||
|
||||
@JsonProperty("min_throughput_iops")
|
||||
private Integer minThroughputIops = null;
|
||||
|
||||
@JsonProperty("name")
|
||||
private String name = null;
|
||||
|
||||
@JsonProperty("uuid")
|
||||
private String uuid = null;
|
||||
|
||||
public Integer getMaxThroughputIops() {
|
||||
return maxThroughputIops;
|
||||
}
|
||||
|
||||
public void setMaxThroughputIops(Integer maxThroughputIops) {
|
||||
this.maxThroughputIops = maxThroughputIops;
|
||||
}
|
||||
|
||||
public Integer getMaxThroughputMbps() {
|
||||
return maxThroughputMbps;
|
||||
}
|
||||
|
||||
public void setMaxThroughputMbps(Integer maxThroughputMbps) {
|
||||
this.maxThroughputMbps = maxThroughputMbps;
|
||||
}
|
||||
|
||||
public Integer getMinThroughputIops() {
|
||||
return minThroughputIops;
|
||||
}
|
||||
|
||||
public void setMinThroughputIops(Integer minThroughputIops) {
|
||||
this.minThroughputIops = minThroughputIops;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class VolumeSpace {
|
||||
@JsonProperty("size")
|
||||
private long size;
|
||||
|
||||
@JsonProperty("available")
|
||||
private long available;
|
||||
|
||||
@JsonProperty("used")
|
||||
private long used;
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(long size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public long getAvailable() {
|
||||
return available;
|
||||
}
|
||||
|
||||
public void setAvailable(long available) {
|
||||
this.available = available;
|
||||
}
|
||||
|
||||
public long getUsed() {
|
||||
return used;
|
||||
}
|
||||
|
||||
public void setUsed(long used) {
|
||||
this.used = used;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class VolumeSpaceLogicalSpace {
|
||||
|
||||
@JsonProperty("available")
|
||||
private Long available = null;
|
||||
|
||||
@JsonProperty("used")
|
||||
private Double used = null;
|
||||
|
||||
public Long getAvailable() {
|
||||
return available;
|
||||
}
|
||||
|
||||
public void setAvailable(Long available) {
|
||||
this.available = available;
|
||||
}
|
||||
|
||||
public Double getUsed() {
|
||||
return used;
|
||||
}
|
||||
|
||||
public void setUsed(Double used) {
|
||||
this.used = used;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.storage.feign.model.response;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.apache.cloudstack.storage.feign.model.Job;
|
||||
|
||||
public class JobResponse {
|
||||
@JsonProperty("job")
|
||||
private Job job;
|
||||
public Job getJob() { return job; }
|
||||
public void setJob(Job job) { this.job = job; }
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.feign.model.response;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* OntapResponse
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class OntapResponse<T> {
|
||||
@JsonProperty("num_records")
|
||||
private Integer numRecords;
|
||||
|
||||
@JsonProperty("records")
|
||||
private List<T> records;
|
||||
|
||||
public OntapResponse () {
|
||||
// Default constructor
|
||||
}
|
||||
|
||||
public OntapResponse (List<T> records) {
|
||||
this.records = records;
|
||||
this.numRecords = (records != null) ? records.size() : 0;
|
||||
}
|
||||
|
||||
public Integer getNumRecords() {
|
||||
return numRecords;
|
||||
}
|
||||
|
||||
public void setNumRecords(Integer numRecords) {
|
||||
this.numRecords = numRecords;
|
||||
}
|
||||
|
||||
public List<T> getRecords() {
|
||||
return records;
|
||||
}
|
||||
|
||||
public void setRecords(List<T> records) {
|
||||
this.records = records;
|
||||
this.numRecords = (records != null) ? records.size() : 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,536 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.lifecycle;
|
||||
|
||||
|
||||
import com.cloud.agent.api.StoragePoolInfo;
|
||||
import com.cloud.dc.ClusterVO;
|
||||
import com.cloud.dc.dao.ClusterDao;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.resource.ResourceManager;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.storage.StorageManager;
|
||||
import com.cloud.storage.StoragePool;
|
||||
import com.cloud.storage.StoragePoolAutomation;
|
||||
import com.cloud.utils.StringUtils;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.HostScope;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCycle;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreParameters;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDetailsDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.storage.datastore.lifecycle.BasePrimaryDataStoreLifeCycleImpl;
|
||||
import org.apache.cloudstack.storage.feign.model.OntapStorage;
|
||||
import org.apache.cloudstack.storage.feign.model.Volume;
|
||||
import org.apache.cloudstack.storage.provider.StorageProviderFactory;
|
||||
import org.apache.cloudstack.storage.service.StorageStrategy;
|
||||
import org.apache.cloudstack.storage.service.model.AccessGroup;
|
||||
import org.apache.cloudstack.storage.service.model.ProtocolType;
|
||||
import org.apache.cloudstack.storage.utils.OntapStorageConstants;
|
||||
import org.apache.cloudstack.storage.utils.OntapStorageUtils;
|
||||
import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class OntapPrimaryDatastoreLifecycle extends BasePrimaryDataStoreLifeCycleImpl implements PrimaryDataStoreLifeCycle {
|
||||
@Inject private ClusterDao _clusterDao;
|
||||
@Inject private StorageManager _storageMgr;
|
||||
@Inject private ResourceManager _resourceMgr;
|
||||
@Inject private PrimaryDataStoreHelper _dataStoreHelper;
|
||||
@Inject private PrimaryDataStoreDetailsDao _datastoreDetailsDao;
|
||||
@Inject private StoragePoolAutomation _storagePoolAutomation;
|
||||
@Inject private PrimaryDataStoreDao storagePoolDao;
|
||||
@Inject private StoragePoolDetailsDao storagePoolDetailsDao;
|
||||
private static final Logger logger = LogManager.getLogger(OntapPrimaryDatastoreLifecycle.class);
|
||||
|
||||
private static final long ONTAP_MIN_VOLUME_SIZE_IN_BYTES = 1677721600L;
|
||||
|
||||
@Override
|
||||
public DataStore initialize(Map<String, Object> dsInfos) {
|
||||
if (dsInfos == null) {
|
||||
throw new CloudRuntimeException("Datastore info map is null, cannot create primary storage");
|
||||
}
|
||||
String url = (String) dsInfos.get("url");
|
||||
Long zoneId = (Long) dsInfos.get("zoneId");
|
||||
Long podId = (Long) dsInfos.get("podId");
|
||||
Long clusterId = (Long) dsInfos.get("clusterId");
|
||||
String storagePoolName = (String) dsInfos.get("name");
|
||||
String providerName = (String) dsInfos.get("providerName");
|
||||
Long capacityBytes = (Long) dsInfos.get("capacityBytes");
|
||||
boolean managed = (boolean) dsInfos.get("managed");
|
||||
String tags = (String) dsInfos.get("tags");
|
||||
Boolean isTagARule = (Boolean) dsInfos.get("isTagARule");
|
||||
|
||||
logger.info("Creating ONTAP primary storage pool with name: " + storagePoolName + ", provider: " + providerName +
|
||||
", zoneId: " + zoneId + ", podId: " + podId + ", clusterId: " + clusterId);
|
||||
logger.debug("Received capacityBytes from UI: " + capacityBytes);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, String> details = (Map<String, String>) dsInfos.get("details");
|
||||
|
||||
capacityBytes = validateInitializeInputs(capacityBytes, podId, clusterId, zoneId, storagePoolName, providerName, managed, url, details);
|
||||
|
||||
PrimaryDataStoreParameters parameters = new PrimaryDataStoreParameters();
|
||||
if (clusterId != null) {
|
||||
ClusterVO clusterVO = _clusterDao.findById(clusterId);
|
||||
Preconditions.checkNotNull(clusterVO, "Unable to locate the specified cluster");
|
||||
if (clusterVO.getHypervisorType() != Hypervisor.HypervisorType.KVM) {
|
||||
throw new CloudRuntimeException("ONTAP primary storage is supported only for KVM hypervisor");
|
||||
}
|
||||
parameters.setHypervisorType(clusterVO.getHypervisorType());
|
||||
}
|
||||
|
||||
details.put(OntapStorageConstants.SIZE, capacityBytes.toString());
|
||||
details.putIfAbsent(OntapStorageConstants.IS_DISAGGREGATED, "false");
|
||||
|
||||
ProtocolType protocol = ProtocolType.valueOf(details.get(OntapStorageConstants.PROTOCOL));
|
||||
|
||||
// long volumeSize = Long.parseLong(details.get(OntapStorageConstants.SIZE));
|
||||
OntapStorage ontapStorage = new OntapStorage(
|
||||
details.get(OntapStorageConstants.USERNAME),
|
||||
details.get(OntapStorageConstants.PASSWORD),
|
||||
details.get(OntapStorageConstants.MANAGEMENT_LIF),
|
||||
details.get(OntapStorageConstants.SVM_NAME),
|
||||
capacityBytes,
|
||||
protocol,
|
||||
Boolean.parseBoolean(details.get(OntapStorageConstants.IS_DISAGGREGATED).toLowerCase()));
|
||||
|
||||
StorageStrategy storageStrategy = StorageProviderFactory.getStrategy(ontapStorage);
|
||||
boolean isValid = storageStrategy.connect();
|
||||
if (isValid) {
|
||||
String dataLIF = storageStrategy.getNetworkInterface();
|
||||
if (dataLIF == null || dataLIF.isEmpty()) {
|
||||
throw new CloudRuntimeException("Failed to retrieve Data LIF from ONTAP, cannot create primary storage");
|
||||
}
|
||||
logger.info("Using Data LIF for storage access: " + dataLIF);
|
||||
details.put(OntapStorageConstants.DATA_LIF, dataLIF);
|
||||
logger.info("Creating ONTAP volume '" + storagePoolName + "' with size: " + capacityBytes + " bytes (" +
|
||||
(capacityBytes / (1024 * 1024 * 1024)) + " GB)");
|
||||
try {
|
||||
Volume volume = storageStrategy.createStorageVolume(storagePoolName, capacityBytes);
|
||||
if (volume == null) {
|
||||
logger.error("createStorageVolume returned null for volume: " + storagePoolName);
|
||||
throw new CloudRuntimeException("Failed to create ONTAP volume: " + storagePoolName);
|
||||
}
|
||||
logger.info("Volume object retrieved successfully. UUID: " + volume.getUuid() + ", Name: " + volume.getName());
|
||||
details.putIfAbsent(OntapStorageConstants.VOLUME_UUID, volume.getUuid());
|
||||
details.putIfAbsent(OntapStorageConstants.VOLUME_NAME, volume.getName());
|
||||
} catch (Exception e) {
|
||||
logger.error("Exception occurred while creating ONTAP volume: " + storagePoolName, e);
|
||||
throw new CloudRuntimeException("Failed to create ONTAP volume: " + storagePoolName + ". Error: " + e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
throw new CloudRuntimeException("ONTAP details validation failed, cannot create primary storage");
|
||||
}
|
||||
|
||||
String path;
|
||||
int port;
|
||||
switch (protocol) {
|
||||
case NFS3:
|
||||
parameters.setType(Storage.StoragePoolType.NetworkFilesystem);
|
||||
path = OntapStorageConstants.SLASH + storagePoolName;
|
||||
port = OntapStorageConstants.NFS3_PORT;
|
||||
logger.info("Setting NFS path for storage pool: " + path + ", port: " + port);
|
||||
break;
|
||||
case ISCSI:
|
||||
parameters.setType(Storage.StoragePoolType.Iscsi);
|
||||
path = storageStrategy.getStoragePath();
|
||||
port = OntapStorageConstants.ISCSI_PORT;
|
||||
logger.info("Setting iSCSI path for storage pool: " + path + ", port: " + port);
|
||||
break;
|
||||
default:
|
||||
throw new CloudRuntimeException("Unsupported protocol: " + protocol + ", cannot create primary storage");
|
||||
}
|
||||
|
||||
parameters.setHost(details.get(OntapStorageConstants.DATA_LIF));
|
||||
parameters.setPort(port);
|
||||
parameters.setPath(path);
|
||||
parameters.setTags(tags);
|
||||
parameters.setIsTagARule(isTagARule);
|
||||
parameters.setDetails(details);
|
||||
parameters.setUuid(UUID.randomUUID().toString());
|
||||
parameters.setZoneId(zoneId);
|
||||
parameters.setPodId(podId);
|
||||
parameters.setClusterId(clusterId);
|
||||
parameters.setName(storagePoolName);
|
||||
parameters.setProviderName(providerName);
|
||||
parameters.setManaged(managed);
|
||||
parameters.setCapacityBytes(capacityBytes);
|
||||
parameters.setUsedBytes(0);
|
||||
|
||||
return _dataStoreHelper.createPrimaryDataStore(parameters);
|
||||
}
|
||||
|
||||
private long validateInitializeInputs(Long capacityBytes, Long podId, Long clusterId, Long zoneId,
|
||||
String storagePoolName, String providerName, boolean managed, String url, Map<String, String> details) {
|
||||
|
||||
// Capacity validation
|
||||
if (capacityBytes == null || capacityBytes <= 0) {
|
||||
logger.warn("capacityBytes not provided or invalid (" + capacityBytes + "), using ONTAP minimum size: " + ONTAP_MIN_VOLUME_SIZE_IN_BYTES);
|
||||
capacityBytes = ONTAP_MIN_VOLUME_SIZE_IN_BYTES;
|
||||
} else if (capacityBytes < ONTAP_MIN_VOLUME_SIZE_IN_BYTES) {
|
||||
logger.warn("capacityBytes (" + capacityBytes + ") is below ONTAP minimum (" + ONTAP_MIN_VOLUME_SIZE_IN_BYTES + "), adjusting to minimum");
|
||||
capacityBytes = ONTAP_MIN_VOLUME_SIZE_IN_BYTES;
|
||||
}
|
||||
|
||||
// Scope (pod/cluster/zone) validation
|
||||
if (podId == null ^ clusterId == null) {
|
||||
throw new CloudRuntimeException("Cluster Id or Pod Id is null, cannot create primary storage");
|
||||
}
|
||||
if (podId == null && clusterId == null) {
|
||||
if (zoneId != null) {
|
||||
logger.info("Both Pod Id and Cluster Id are null, Primary storage pool will be associated with a Zone");
|
||||
} else {
|
||||
throw new CloudRuntimeException("Pod Id, Cluster Id and Zone Id are all null, cannot create primary storage");
|
||||
}
|
||||
}
|
||||
|
||||
// Basic parameter validation
|
||||
if (StringUtils.isBlank(storagePoolName)) {
|
||||
throw new CloudRuntimeException("Storage pool name is null or empty, cannot create primary storage");
|
||||
}
|
||||
if (StringUtils.isBlank(providerName)) {
|
||||
throw new CloudRuntimeException("Provider name is null or empty, cannot create primary storage");
|
||||
}
|
||||
logger.debug("ONTAP primary storage will be created as " + (managed ? "managed" : "unmanaged"));
|
||||
if (!managed) {
|
||||
throw new CloudRuntimeException("ONTAP primary storage must be managed");
|
||||
}
|
||||
|
||||
// Details key validation
|
||||
Set<String> requiredKeys = Set.of(
|
||||
OntapStorageConstants.USERNAME,
|
||||
OntapStorageConstants.PASSWORD,
|
||||
OntapStorageConstants.SVM_NAME,
|
||||
OntapStorageConstants.PROTOCOL,
|
||||
OntapStorageConstants.MANAGEMENT_LIF
|
||||
);
|
||||
Set<String> optionalKeys = Set.of(
|
||||
OntapStorageConstants.IS_DISAGGREGATED
|
||||
);
|
||||
Set<String> allowedKeys = new java.util.HashSet<>(requiredKeys);
|
||||
allowedKeys.addAll(optionalKeys);
|
||||
|
||||
if (StringUtils.isNotBlank(url)) {
|
||||
for (String segment : url.split(OntapStorageConstants.SEMICOLON)) {
|
||||
if (segment.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
String[] kv = segment.split(OntapStorageConstants.EQUALS, 2);
|
||||
if (kv.length == 2) {
|
||||
details.put(kv[0].trim(), kv[1].trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> e : details.entrySet()) {
|
||||
String key = e.getKey();
|
||||
String val = e.getValue();
|
||||
if (!allowedKeys.contains(key)) {
|
||||
throw new CloudRuntimeException("Unexpected ONTAP detail key in URL: " + key);
|
||||
}
|
||||
if (StringUtils.isBlank(val)) {
|
||||
throw new CloudRuntimeException("ONTAP primary storage creation failed, empty detail: " + key);
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> providedKeys = new HashSet<>(details.keySet());
|
||||
if (!providedKeys.containsAll(requiredKeys)) {
|
||||
Set<String> missing = new HashSet<>(requiredKeys);
|
||||
missing.removeAll(providedKeys);
|
||||
throw new CloudRuntimeException("ONTAP primary storage creation failed, missing detail(s): " + missing);
|
||||
}
|
||||
|
||||
return capacityBytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean attachCluster(DataStore dataStore, ClusterScope scope) {
|
||||
logger.debug("In attachCluster for ONTAP primary storage");
|
||||
if (dataStore == null) {
|
||||
throw new InvalidParameterValueException("attachCluster: dataStore should not be null");
|
||||
}
|
||||
if (scope == null) {
|
||||
throw new InvalidParameterValueException("attachCluster: scope should not be null");
|
||||
}
|
||||
List<String> hostsIdentifier = new ArrayList<>();
|
||||
StoragePoolVO storagePool = storagePoolDao.findById(dataStore.getId());
|
||||
if (storagePool == null) {
|
||||
logger.error("attachCluster : Storage Pool not found for id: " + dataStore.getId());
|
||||
throw new CloudRuntimeException("attachCluster : Storage Pool not found for id: " + dataStore.getId());
|
||||
}
|
||||
PrimaryDataStoreInfo primaryStore = (PrimaryDataStoreInfo)dataStore;
|
||||
List<HostVO> hostsToConnect = _resourceMgr.getEligibleUpAndEnabledHostsInClusterForStorageConnection(primaryStore);
|
||||
logger.debug("attachCluster: Eligible Up and Enabled hosts: {} in cluster {}", hostsToConnect, primaryStore.getClusterId());
|
||||
|
||||
Map<String, String> details = storagePoolDetailsDao.listDetailsKeyPairs(primaryStore.getId());
|
||||
StorageStrategy strategy = OntapStorageUtils.getStrategyByStoragePoolDetails(details);
|
||||
|
||||
ProtocolType protocol = ProtocolType.valueOf(details.get(OntapStorageConstants.PROTOCOL));
|
||||
if (!validateProtocolSupportAndFetchHostsIdentifier(hostsToConnect, protocol, hostsIdentifier)) {
|
||||
String errMsg = "attachCluster: Not all hosts in the cluster support the protocol: " + protocol.name();
|
||||
logger.error(errMsg);
|
||||
throw new CloudRuntimeException(errMsg);
|
||||
}
|
||||
|
||||
logger.debug("attachCluster: Attaching the pool to each of the host in the cluster: {}", primaryStore.getClusterId());
|
||||
if (hostsIdentifier != null && hostsIdentifier.size() > 0) {
|
||||
try {
|
||||
AccessGroup accessGroupRequest = new AccessGroup();
|
||||
accessGroupRequest.setHostsToConnect(hostsToConnect);
|
||||
accessGroupRequest.setScope(scope);
|
||||
primaryStore.setDetails(details);
|
||||
accessGroupRequest.setPrimaryDataStoreInfo(primaryStore);
|
||||
strategy.createAccessGroup(accessGroupRequest);
|
||||
} catch (Exception e) {
|
||||
logger.error("attachCluster: Failed to create access group on storage system for cluster: " + primaryStore.getClusterId() + ". Exception: " + e.getMessage());
|
||||
throw new CloudRuntimeException("attachCluster: Failed to create access group on storage system for cluster: " + primaryStore.getClusterId() + ". Exception: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
logger.debug("attachCluster: Attaching the pool to each of the host in the cluster: {}", primaryStore.getClusterId());
|
||||
for (HostVO host : hostsToConnect) {
|
||||
try {
|
||||
_storageMgr.connectHostToSharedPool(host, dataStore.getId());
|
||||
} catch (Exception e) {
|
||||
logger.warn("attachCluster: Unable to establish a connection between " + host + " and " + dataStore, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_dataStoreHelper.attachCluster(dataStore);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean attachHost(DataStore store, HostScope scope, StoragePoolInfo existingInfo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean attachZone(DataStore dataStore, ZoneScope scope, Hypervisor.HypervisorType hypervisorType) {
|
||||
logger.debug("In attachZone for ONTAP primary storage");
|
||||
if (dataStore == null) {
|
||||
throw new InvalidParameterValueException("attachZone: dataStore should not be null");
|
||||
}
|
||||
if (scope == null) {
|
||||
throw new InvalidParameterValueException("attachZone: scope should not be null");
|
||||
}
|
||||
List<String> hostsIdentifier = new ArrayList<>();
|
||||
StoragePoolVO storagePool = storagePoolDao.findById(dataStore.getId());
|
||||
if (storagePool == null) {
|
||||
logger.error("attachZone : Storage Pool not found for id: " + dataStore.getId());
|
||||
throw new CloudRuntimeException("attachZone : Storage Pool not found for id: " + dataStore.getId());
|
||||
}
|
||||
|
||||
PrimaryDataStoreInfo primaryStore = (PrimaryDataStoreInfo)dataStore;
|
||||
List<HostVO> hostsToConnect = _resourceMgr.getEligibleUpAndEnabledHostsInZoneForStorageConnection(dataStore, scope.getScopeId(), Hypervisor.HypervisorType.KVM);
|
||||
logger.debug(String.format("In createPool. Attaching the pool to each of the hosts in %s.", hostsToConnect));
|
||||
|
||||
Map<String, String> details = storagePoolDetailsDao.listDetailsKeyPairs(primaryStore.getId());
|
||||
StorageStrategy strategy = OntapStorageUtils.getStrategyByStoragePoolDetails(details);
|
||||
|
||||
logger.debug("attachZone: Eligible Up and Enabled hosts: {}", hostsToConnect);
|
||||
ProtocolType protocol = ProtocolType.valueOf(details.get(OntapStorageConstants.PROTOCOL));
|
||||
if (!validateProtocolSupportAndFetchHostsIdentifier(hostsToConnect, protocol, hostsIdentifier)) {
|
||||
String errMsg = "attachZone: Not all hosts in the zone support the protocol: " + protocol.name();
|
||||
logger.error(errMsg);
|
||||
throw new CloudRuntimeException(errMsg);
|
||||
}
|
||||
if (hostsIdentifier != null && !hostsIdentifier.isEmpty()) {
|
||||
try {
|
||||
AccessGroup accessGroupRequest = new AccessGroup();
|
||||
accessGroupRequest.setHostsToConnect(hostsToConnect);
|
||||
accessGroupRequest.setScope(scope);
|
||||
primaryStore.setDetails(details);
|
||||
accessGroupRequest.setPrimaryDataStoreInfo(primaryStore);
|
||||
strategy.createAccessGroup(accessGroupRequest);
|
||||
} catch (Exception e) {
|
||||
logger.error("attachZone: Failed to create access group on storage system for zone with Exception: " + e.getMessage());
|
||||
throw new CloudRuntimeException("attachZone: Failed to create access group on storage system for zone with Exception: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
for (HostVO host : hostsToConnect) {
|
||||
try {
|
||||
_storageMgr.connectHostToSharedPool(host, dataStore.getId());
|
||||
} catch (Exception e) {
|
||||
logger.warn("Unable to establish a connection between " + host + " and " + dataStore, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_dataStoreHelper.attachZone(dataStore);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean validateProtocolSupportAndFetchHostsIdentifier(List<HostVO> hosts, ProtocolType protocolType, List<String> hostIdentifiers) {
|
||||
switch (protocolType) {
|
||||
case ISCSI:
|
||||
String protocolPrefix = OntapStorageConstants.IQN;
|
||||
for (HostVO host : hosts) {
|
||||
if (host == null || host.getStorageUrl() == null || host.getStorageUrl().trim().isEmpty()
|
||||
|| !host.getStorageUrl().startsWith(protocolPrefix)) {
|
||||
return false;
|
||||
}
|
||||
hostIdentifiers.add(host.getStorageUrl());
|
||||
}
|
||||
break;
|
||||
case NFS3:
|
||||
String ip = "";
|
||||
for (HostVO host : hosts) {
|
||||
if (host != null) {
|
||||
ip = host.getStorageIpAddress() != null ? host.getStorageIpAddress().trim() : "";
|
||||
if (ip.isEmpty()) {
|
||||
if (host.getPrivateIpAddress() == null || host.getPrivateIpAddress().trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
ip = host.getPrivateIpAddress().trim();
|
||||
}
|
||||
}
|
||||
hostIdentifiers.add(ip);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new CloudRuntimeException("validateProtocolSupportAndFetchHostsIdentifier : Unsupported protocol: " + protocolType.name());
|
||||
}
|
||||
logger.info("validateProtocolSupportAndFetchHostsIdentifier: All hosts support the protocol: " + protocolType.name());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean maintain(DataStore store) {
|
||||
logger.info("Placing storage pool {} in maintenance mode", store);
|
||||
if (_storagePoolAutomation.maintain(store)) {
|
||||
return _dataStoreHelper.maintain(store);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancelMaintain(DataStore store) {
|
||||
logger.info("Cancelling storage pool maintenance for {}", store);
|
||||
if (_dataStoreHelper.cancelMaintain(store)) {
|
||||
return _storagePoolAutomation.cancelMaintain(store);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteDataStore(DataStore store) {
|
||||
logger.info("deleteDataStore: Starting deletion process for storage pool id: {}", store.getId());
|
||||
|
||||
long storagePoolId = store.getId();
|
||||
StoragePool storagePool = _storageMgr.getStoragePool(storagePoolId);
|
||||
if (storagePool == null) {
|
||||
logger.warn("deleteDataStore: Storage pool not found for id: {}, skipping deletion", storagePoolId);
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
Map<String, String> details = _datastoreDetailsDao.listDetailsKeyPairs(storagePoolId);
|
||||
if (details == null || details.isEmpty()) {
|
||||
logger.warn("deleteDataStore: No details found for storage pool id: {}, proceeding with CS entity deletion only", storagePoolId);
|
||||
return _dataStoreHelper.deletePrimaryDataStore(store);
|
||||
}
|
||||
|
||||
logger.info("deleteDataStore: Deleting access groups for storage pool '{}'", storagePool.getName());
|
||||
|
||||
StorageStrategy storageStrategy = OntapStorageUtils.getStrategyByStoragePoolDetails(details);
|
||||
|
||||
PrimaryDataStoreInfo primaryDataStoreInfo = (PrimaryDataStoreInfo) store;
|
||||
primaryDataStoreInfo.setDetails(details);
|
||||
|
||||
logger.info("deleteDataStore: Deleting ONTAP volume for storage pool '{}'", storagePool.getName());
|
||||
Volume volume = new Volume();
|
||||
volume.setUuid(details.get(OntapStorageConstants.VOLUME_UUID));
|
||||
volume.setName(details.get(OntapStorageConstants.VOLUME_NAME));
|
||||
try {
|
||||
if (volume.getUuid() == null || volume.getUuid().isEmpty() || volume.getName() == null || volume.getName().isEmpty()) {
|
||||
logger.error("deleteDataStore: Volume UUID/Name not found in details for storage pool id: {}, cannot delete volume", storagePoolId);
|
||||
throw new CloudRuntimeException("Volume UUID/Name not found in details, cannot delete ONTAP volume");
|
||||
}
|
||||
storageStrategy.deleteStorageVolume(volume);
|
||||
logger.info("deleteDataStore: Successfully deleted ONTAP volume '{}' (UUID: {}) for storage pool '{}'",
|
||||
volume.getName(), volume.getUuid(), storagePool.getName());
|
||||
} catch (Exception e) {
|
||||
logger.error("deleteDataStore: Exception while retrieving volume UUID for storage pool id: {}. Error: {}",
|
||||
storagePoolId, e.getMessage(), e);
|
||||
}
|
||||
AccessGroup accessGroup = new AccessGroup();
|
||||
accessGroup.setPrimaryDataStoreInfo(primaryDataStoreInfo);
|
||||
storageStrategy.deleteAccessGroup(accessGroup);
|
||||
logger.info("deleteDataStore: Successfully deleted access groups for storage pool '{}'", storagePool.getName());
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("deleteDataStore: Failed to delete access groups for storage pool id: {}. Error: {}",
|
||||
storagePoolId, e.getMessage(), e);
|
||||
logger.warn("deleteDataStore: Proceeding with CloudStack entity deletion despite ONTAP cleanup failure");
|
||||
}
|
||||
|
||||
return _dataStoreHelper.deletePrimaryDataStore(store);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean migrateToObjectStore(DataStore store) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStoragePool(StoragePool storagePool, Map<String, String> details) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableStoragePool(DataStore store) {
|
||||
_dataStoreHelper.enable(store);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableStoragePool(DataStore store) {
|
||||
_dataStoreHelper.disable(store);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeStoragePoolScopeToZone(DataStore store, ClusterScope clusterScope, Hypervisor.HypervisorType hypervisorType) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeStoragePoolScopeToCluster(DataStore store, ClusterScope clusterScope, Hypervisor.HypervisorType hypervisorType) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.listener;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.agent.api.ModifyStoragePoolCommand;
|
||||
import com.cloud.agent.api.ModifyStoragePoolAnswer;
|
||||
import com.cloud.agent.api.StoragePoolInfo;
|
||||
import com.cloud.alert.AlertManager;
|
||||
import com.cloud.storage.StoragePoolHostVO;
|
||||
import com.cloud.storage.dao.StoragePoolHostDao;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import com.cloud.agent.AgentManager;
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.DeleteStoragePoolCommand;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.storage.StoragePool;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
|
||||
public class OntapHostListener implements HypervisorHostListener {
|
||||
protected Logger logger = LogManager.getLogger(getClass());
|
||||
|
||||
@Inject
|
||||
private AgentManager _agentMgr;
|
||||
@Inject
|
||||
private AlertManager _alertMgr;
|
||||
@Inject
|
||||
private PrimaryDataStoreDao _storagePoolDao;
|
||||
@Inject
|
||||
private HostDao _hostDao;
|
||||
@Inject
|
||||
private StoragePoolHostDao storagePoolHostDao;
|
||||
|
||||
@Override
|
||||
public boolean hostConnect(long hostId, long poolId) {
|
||||
logger.info("Connect to host " + hostId + " from pool " + poolId);
|
||||
Host host = _hostDao.findById(hostId);
|
||||
if (host == null) {
|
||||
logger.error("host was not found with id : {}", hostId);
|
||||
return false;
|
||||
}
|
||||
|
||||
StoragePool pool = _storagePoolDao.findById(poolId);
|
||||
if (pool == null) {
|
||||
logger.error("Failed to connect host - storage pool not found with id: {}", poolId);
|
||||
return false;
|
||||
}
|
||||
logger.info("Connecting host {} to ONTAP storage pool {}", host.getName(), pool.getName());
|
||||
try {
|
||||
ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(true, pool);
|
||||
|
||||
Answer answer = _agentMgr.easySend(hostId, cmd);
|
||||
|
||||
if (answer == null) {
|
||||
throw new CloudRuntimeException(String.format("Unable to get an answer to the modify storage pool command (%s)", pool));
|
||||
}
|
||||
|
||||
if (!answer.getResult()) {
|
||||
String msg = String.format("Unable to attach storage pool %s to host %d", pool, hostId);
|
||||
|
||||
_alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, pool.getDataCenterId(), pool.getPodId(), msg, msg);
|
||||
|
||||
throw new CloudRuntimeException(String.format(
|
||||
"Unable to establish a connection from agent to storage pool %s due to %s", pool, answer.getDetails()));
|
||||
}
|
||||
|
||||
if (!(answer instanceof ModifyStoragePoolAnswer)) {
|
||||
logger.error("Received unexpected answer type {} for storage pool {}", answer.getClass().getName(), pool.getName());
|
||||
throw new CloudRuntimeException("Failed to connect to storage pool. Please check agent logs for details.");
|
||||
}
|
||||
|
||||
ModifyStoragePoolAnswer mspAnswer = (ModifyStoragePoolAnswer) answer;
|
||||
StoragePoolInfo poolInfo = mspAnswer.getPoolInfo();
|
||||
if (poolInfo == null) {
|
||||
throw new CloudRuntimeException("ModifyStoragePoolAnswer returned null poolInfo");
|
||||
}
|
||||
|
||||
String localPath = poolInfo.getLocalPath();
|
||||
logger.info("Storage pool {} successfully mounted at: {}", pool.getName(), localPath);
|
||||
|
||||
StoragePoolHostVO storagePoolHost = storagePoolHostDao.findByPoolHost(poolId, hostId);
|
||||
|
||||
if (storagePoolHost == null) {
|
||||
storagePoolHost = new StoragePoolHostVO(poolId, hostId, localPath);
|
||||
storagePoolHostDao.persist(storagePoolHost);
|
||||
logger.info("Created storage_pool_host_ref entry for pool {} and host {}", pool.getName(), host.getName());
|
||||
} else {
|
||||
storagePoolHost.setLocalPath(localPath);
|
||||
storagePoolHostDao.update(storagePoolHost.getId(), storagePoolHost);
|
||||
logger.info("Updated storage_pool_host_ref entry with local_path: {}", localPath);
|
||||
}
|
||||
|
||||
StoragePoolVO poolVO = _storagePoolDao.findById(poolId);
|
||||
if (poolVO != null && poolInfo.getCapacityBytes() > 0) {
|
||||
poolVO.setCapacityBytes(poolInfo.getCapacityBytes());
|
||||
poolVO.setUsedBytes(poolInfo.getCapacityBytes() - poolInfo.getAvailableBytes());
|
||||
_storagePoolDao.update(poolVO.getId(), poolVO);
|
||||
logger.info("Updated storage pool capacity: {} GB, used: {} GB", poolInfo.getCapacityBytes() / (1024 * 1024 * 1024), (poolInfo.getCapacityBytes() - poolInfo.getAvailableBytes()) / (1024 * 1024 * 1024));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Exception while connecting host {} to storage pool {}", host.getName(), pool.getName(), e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hostDisconnected(Host host, StoragePool pool) {
|
||||
logger.info("Disconnect from host " + host.getId() + " from pool " + pool.getName());
|
||||
|
||||
Host hostToremove = _hostDao.findById(host.getId());
|
||||
if (hostToremove == null) {
|
||||
logger.error("Failed to add host by HostListener as host was not found with id : {}", host.getId());
|
||||
return false;
|
||||
}
|
||||
logger.info("Disconnecting host {} from ONTAP storage pool {}", host.getName(), pool.getName());
|
||||
|
||||
try {
|
||||
DeleteStoragePoolCommand cmd = new DeleteStoragePoolCommand(pool);
|
||||
long hostId = host.getId();
|
||||
Answer answer = _agentMgr.easySend(hostId, cmd);
|
||||
|
||||
if (answer != null && answer.getResult()) {
|
||||
logger.info("Successfully disconnected host {} from ONTAP storage pool {}", host.getName(), pool.getName());
|
||||
return true;
|
||||
} else {
|
||||
String errMsg = (answer != null) ? answer.getDetails() : "Unknown error";
|
||||
logger.warn("Failed to disconnect host {} from storage pool {}. Error: {}", host.getName(), pool.getName(), errMsg);
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Exception while disconnecting host {} from storage pool {}", host.getName(), pool.getName(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hostDisconnected(long hostId, long poolId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hostAboutToBeRemoved(long hostId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hostRemoved(long hostId, long clusterId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hostEnabled(long hostId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hostAdded(long hostId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.provider;
|
||||
|
||||
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreLifeCycle;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreProvider;
|
||||
import org.apache.cloudstack.storage.driver.OntapPrimaryDatastoreDriver;
|
||||
import org.apache.cloudstack.storage.lifecycle.OntapPrimaryDatastoreLifecycle;
|
||||
import org.apache.cloudstack.storage.listener.OntapHostListener;
|
||||
import org.apache.cloudstack.storage.utils.OntapStorageConstants;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Component
|
||||
public class OntapPrimaryDatastoreProvider implements PrimaryDataStoreProvider {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(OntapPrimaryDatastoreProvider.class);
|
||||
private OntapPrimaryDatastoreDriver primaryDatastoreDriver;
|
||||
private OntapPrimaryDatastoreLifecycle primaryDatastoreLifecycle;
|
||||
private HypervisorHostListener listener;
|
||||
|
||||
public OntapPrimaryDatastoreProvider() {
|
||||
logger.info("OntapPrimaryDatastoreProvider initialized");
|
||||
}
|
||||
@Override
|
||||
public DataStoreLifeCycle getDataStoreLifeCycle() {
|
||||
return primaryDatastoreLifecycle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataStoreDriver getDataStoreDriver() {
|
||||
return primaryDatastoreDriver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HypervisorHostListener getHostListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
logger.trace("OntapPrimaryDatastoreProvider: getName: Called");
|
||||
return OntapStorageConstants.ONTAP_PLUGIN_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean configure(Map<String, Object> params) {
|
||||
logger.trace("OntapPrimaryDatastoreProvider: configure: Called");
|
||||
primaryDatastoreDriver = ComponentContext.inject(OntapPrimaryDatastoreDriver.class);
|
||||
primaryDatastoreLifecycle = ComponentContext.inject(OntapPrimaryDatastoreLifecycle.class);
|
||||
listener = ComponentContext.inject(OntapHostListener.class);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DataStoreProviderType> getTypes() {
|
||||
logger.trace("OntapPrimaryDatastoreProvider: getTypes: Called");
|
||||
Set<DataStoreProviderType> typeSet = new HashSet<DataStoreProviderType>();
|
||||
typeSet.add(DataStoreProviderType.PRIMARY);
|
||||
return typeSet;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.provider;
|
||||
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.cloudstack.storage.feign.model.OntapStorage;
|
||||
import org.apache.cloudstack.storage.service.StorageStrategy;
|
||||
import org.apache.cloudstack.storage.service.UnifiedNASStrategy;
|
||||
import org.apache.cloudstack.storage.service.UnifiedSANStrategy;
|
||||
import org.apache.cloudstack.storage.service.model.ProtocolType;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
||||
public class StorageProviderFactory {
|
||||
private static final Logger logger = LogManager.getLogger(StorageProviderFactory.class);
|
||||
|
||||
public static StorageStrategy getStrategy(OntapStorage ontapStorage) {
|
||||
ProtocolType protocol = ontapStorage.getProtocol();
|
||||
logger.info("Initializing StorageProviderFactory with protocol: " + protocol);
|
||||
switch (protocol) {
|
||||
case NFS3:
|
||||
if (!ontapStorage.getIsDisaggregated()) {
|
||||
UnifiedNASStrategy unifiedNASStrategy = new UnifiedNASStrategy(ontapStorage);
|
||||
ComponentContext.inject(unifiedNASStrategy);
|
||||
unifiedNASStrategy.setOntapStorage(ontapStorage);
|
||||
return unifiedNASStrategy;
|
||||
}
|
||||
throw new CloudRuntimeException("Unsupported configuration: Disaggregated ONTAP is not supported.");
|
||||
case ISCSI:
|
||||
if (!ontapStorage.getIsDisaggregated()) {
|
||||
UnifiedSANStrategy unifiedSANStrategy = new UnifiedSANStrategy(ontapStorage);
|
||||
ComponentContext.inject(unifiedSANStrategy);
|
||||
unifiedSANStrategy.setOntapStorage(ontapStorage);
|
||||
return unifiedSANStrategy;
|
||||
}
|
||||
throw new CloudRuntimeException("Unsupported configuration: Disaggregated ONTAP is not supported.");
|
||||
default:
|
||||
throw new CloudRuntimeException("Unsupported protocol: " + protocol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.service;
|
||||
|
||||
import org.apache.cloudstack.storage.feign.model.OntapStorage;
|
||||
|
||||
public abstract class NASStrategy extends StorageStrategy {
|
||||
public NASStrategy(OntapStorage ontapStorage) {
|
||||
super(ontapStorage);
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue