PowerFlex/ScaleIO bugfix Jan Patch (#110)
* Addressed some issues for the operations on PowerFlex storage pool.
- Updated VM Snapshot naming, for uniqueness in ScaleIO volume name when more than one volume exists in the VM.
- Added sync lock while spooling managed storage template before volume creation from the template (non-direct download).
- Updated resize volume error message string.
- Blocked the below operations on PowerFlex storage pool:
-> Extract Volume
-> Create Snapshot for VMSnapshot
* Fail the VM deployment when the host specified in the deployVirtualMachine cmd is not in the right state (i.e. either Resource State is not Enabled or Status is not Up)
* Use single gateway client per Powerflex/ScaleIO pool and renew it when the session token expires.
The token is valid for 8 hours from the time it was created, unless there has been no activity for 10 minutes.
Reference: https://cpsdocs.dellemc.com/bundle/PF_REST_API_RG/page/GUID-92430F19-9F44-42B6-B898-87D5307AE59B.html
* Use the physical file size of the template to check the free space availability on the host, while downloading the direct download templates.
* Perform basic tests (for connectivity and file system) on router before updating the health check config data
Updated changes to:
- Validate the basic tests (connectivity and file system check) on router
- Cleanup the health check results when router is destroyed
diff --git a/api/src/main/java/com/cloud/network/VirtualNetworkApplianceService.java b/api/src/main/java/com/cloud/network/VirtualNetworkApplianceService.java
index 98fb8be..8504efd 100644
--- a/api/src/main/java/com/cloud/network/VirtualNetworkApplianceService.java
+++ b/api/src/main/java/com/cloud/network/VirtualNetworkApplianceService.java
@@ -26,6 +26,7 @@
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.router.VirtualRouter;
import com.cloud.user.Account;
+import com.cloud.utils.Pair;
public interface VirtualNetworkApplianceService {
/**
@@ -73,5 +74,5 @@
* @param routerId id of the router
* @return
*/
- boolean performRouterHealthChecks(long routerId);
+ Pair<Boolean, String> performRouterHealthChecks(long routerId);
}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/GetRouterHealthCheckResultsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/GetRouterHealthCheckResultsCmd.java
index 5efc6de..dc1020b 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/GetRouterHealthCheckResultsCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/GetRouterHealthCheckResultsCmd.java
@@ -111,7 +111,7 @@
setResponseObject(routerResponse);
} catch (CloudRuntimeException ex){
ex.printStackTrace();
- throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to execute command due to exception: " + ex.getLocalizedMessage());
+ throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to get health check results due to: " + ex.getLocalizedMessage());
}
}
}
diff --git a/core/src/main/java/com/cloud/agent/api/routing/GetRouterMonitorResultsCommand.java b/core/src/main/java/com/cloud/agent/api/routing/GetRouterMonitorResultsCommand.java
index 779a0f4..e32dda3 100644
--- a/core/src/main/java/com/cloud/agent/api/routing/GetRouterMonitorResultsCommand.java
+++ b/core/src/main/java/com/cloud/agent/api/routing/GetRouterMonitorResultsCommand.java
@@ -19,12 +19,14 @@
public class GetRouterMonitorResultsCommand extends NetworkElementCommand {
private boolean performFreshChecks;
+ private boolean validateBasicTestsOnly;
protected GetRouterMonitorResultsCommand() {
}
- public GetRouterMonitorResultsCommand(boolean performFreshChecks) {
+ public GetRouterMonitorResultsCommand(boolean performFreshChecks, boolean validateBasicTestsOnly) {
this.performFreshChecks = performFreshChecks;
+ this.validateBasicTestsOnly = validateBasicTestsOnly;
}
@Override
@@ -35,4 +37,8 @@
public boolean shouldPerformFreshChecks() {
return performFreshChecks;
}
+
+ public boolean shouldValidateBasicTestsOnly() {
+ return validateBasicTestsOnly;
+ }
}
\ No newline at end of file
diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java
index 8b32842..395a174 100644
--- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java
+++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java
@@ -66,6 +66,7 @@
import com.cloud.agent.resource.virtualnetwork.facade.AbstractConfigItemFacade;
import com.cloud.utils.ExecutionResult;
import com.cloud.utils.NumbersUtil;
+import com.cloud.utils.Pair;
import com.cloud.utils.exception.CloudRuntimeException;
/**
@@ -312,20 +313,14 @@
private GetRouterMonitorResultsAnswer execute(GetRouterMonitorResultsCommand cmd) {
String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP);
- ExecutionResult fsReadOnlyResult = _vrDeployer.executeInVR(routerIp, VRScripts.ROUTER_FILESYSTEM_WRITABLE_CHECK, null);
- if (!fsReadOnlyResult.isSuccess()) {
- s_logger.warn("Result of " + cmd + " failed with details: " + fsReadOnlyResult.getDetails());
- if (StringUtils.isNotBlank(fsReadOnlyResult.getDetails())) {
- final String readOnlyFileSystemError = "Read-only file system";
- if (fsReadOnlyResult.getDetails().contains(readOnlyFileSystemError)) {
- return new GetRouterMonitorResultsAnswer(cmd, false, null, readOnlyFileSystemError);
- } else {
- return new GetRouterMonitorResultsAnswer(cmd, false, null, fsReadOnlyResult.getDetails());
- }
- } else {
- s_logger.warn("Result of " + cmd + " received empty details.");
- return new GetRouterMonitorResultsAnswer(cmd, false, null, "No results available.");
- }
+ Pair<Boolean, String> fileSystemTestResult = checkRouterFileSystem(routerIp);
+ if (!fileSystemTestResult.first()) {
+ return new GetRouterMonitorResultsAnswer(cmd, false, null, fileSystemTestResult.second());
+ }
+
+ if (cmd.shouldValidateBasicTestsOnly()) {
+ // Basic tests (connectivity and file system checks) are already validated
+ return new GetRouterMonitorResultsAnswer(cmd, true, null, "success");
}
String args = cmd.shouldPerformFreshChecks() ? "true" : "false";
@@ -345,6 +340,27 @@
return parseLinesForHealthChecks(cmd, result.getDetails());
}
+ private Pair<Boolean, String> checkRouterFileSystem(String routerIp) {
+ ExecutionResult fileSystemWritableTestResult = _vrDeployer.executeInVR(routerIp, VRScripts.ROUTER_FILESYSTEM_WRITABLE_CHECK, null);
+ if (fileSystemWritableTestResult.isSuccess()) {
+ s_logger.debug("Router connectivity and file system writable check passed");
+ return new Pair<Boolean, String>(true, "success");
+ }
+
+ String resultDetails = fileSystemWritableTestResult.getDetails();
+ s_logger.warn("File system writable check failed with details: " + resultDetails);
+ if (StringUtils.isNotBlank(resultDetails)) {
+ final String readOnlyFileSystemError = "Read-only file system";
+ if (resultDetails.contains(readOnlyFileSystemError)) {
+ resultDetails = "Read-only file system";
+ }
+ } else {
+ resultDetails = "No results available";
+ }
+
+ return new Pair<Boolean, String>(false, resultDetails);
+ }
+
private GetRouterAlertsAnswer execute(GetRouterAlertsCommand cmd) {
String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP);
diff --git a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java
index 316926a..13eccd7 100644
--- a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java
+++ b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java
@@ -122,7 +122,7 @@
"storage.pool.client.timeout",
"Storage",
"60",
- "Timeout (in secs) for the storage pool client timeout (for managed pools). Currently only supported for PowerFlex.",
+ "Timeout (in secs) for the storage pool client connection timeout (for managed pools). Currently only supported for PowerFlex.",
true,
ConfigKey.Scope.StoragePool,
null);
diff --git a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/ScaleIOVMSnapshotStrategy.java b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/ScaleIOVMSnapshotStrategy.java
index b1fcf5c..b837e60 100644
--- a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/ScaleIOVMSnapshotStrategy.java
+++ b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/ScaleIOVMSnapshotStrategy.java
@@ -31,6 +31,7 @@
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.storage.datastore.api.SnapshotGroup;
import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClient;
+import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClientConnectionPool;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
@@ -46,14 +47,12 @@
import com.cloud.server.ManagementServerImpl;
import com.cloud.storage.DiskOfferingVO;
import com.cloud.storage.Storage;
-import com.cloud.storage.StorageManager;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.uservm.UserVm;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.component.ManagerBase;
-import com.cloud.utils.crypt.DBEncryptionUtil;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn;
@@ -140,7 +139,7 @@
long prev_chain_size = 0;
long virtual_size=0;
for (VolumeObjectTO volume : volumeTOs) {
- String volumeSnapshotName = String.format("%s-%s-%s-%s", ScaleIOUtil.VMSNAPSHOT_PREFIX, vmSnapshotVO.getId(),
+ String volumeSnapshotName = String.format("%s-%s-%s-%s-%s", ScaleIOUtil.VMSNAPSHOT_PREFIX, vmSnapshotVO.getId(), volume.getId(),
storagePool.getUuid().split("-")[0].substring(4), ManagementServerImpl.customCsIdentifier.value());
srcVolumeDestSnapshotMap.put(volume.getPath(), volumeSnapshotName);
@@ -476,12 +475,6 @@
}
private ScaleIOGatewayClient getScaleIOClient(final Long storagePoolId) throws Exception {
- final int clientTimeout = StorageManager.STORAGE_POOL_CLIENT_TIMEOUT.valueIn(storagePoolId);
- final String url = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_ENDPOINT).getValue();
- final String encryptedUsername = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_USERNAME).getValue();
- final String username = DBEncryptionUtil.decrypt(encryptedUsername);
- final String encryptedPassword = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_PASSWORD).getValue();
- final String password = DBEncryptionUtil.decrypt(encryptedPassword);
- return ScaleIOGatewayClient.getClient(url, username, password, false, clientTimeout);
+ return ScaleIOGatewayClientConnectionPool.getInstance().getClient(storagePoolId, storagePoolDetailsDao);
}
}
diff --git a/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java b/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
index 7c4601e..9d35911 100644
--- a/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
+++ b/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
@@ -1384,25 +1384,47 @@
if (storageCanCloneVolume && computeSupportsVolumeClone) {
s_logger.debug("Storage " + destDataStoreId + " can support cloning using a cached template and compute side is OK with volume cloning.");
- TemplateInfo templateOnPrimary = destPrimaryDataStore.getTemplate(srcTemplateInfo.getId());
+ GlobalLock lock = null;
+ TemplateInfo templateOnPrimary = null;
- if (templateOnPrimary == null) {
- templateOnPrimary = createManagedTemplateVolume(srcTemplateInfo, destPrimaryDataStore);
+ try {
+ String tmplIdManagedPoolIdLockString = "tmplId:" + srcTemplateInfo.getId() + "managedPoolId:" + destDataStoreId;
+ lock = GlobalLock.getInternLock(tmplIdManagedPoolIdLockString);
+ if (lock == null) {
+ throw new CloudRuntimeException("Unable to create managed storage template/volume, couldn't get global lock on " + tmplIdManagedPoolIdLockString);
+ }
+
+ int storagePoolMaxWaitSeconds = NumbersUtil.parseInt(configDao.getValue(Config.StoragePoolMaxWaitSeconds.key()), 3600);
+ if (!lock.lock(storagePoolMaxWaitSeconds)) {
+ s_logger.debug("Unable to create managed storage template/volume, couldn't lock on " + tmplIdManagedPoolIdLockString);
+ throw new CloudRuntimeException("Unable to create managed storage template/volume, couldn't lock on " + tmplIdManagedPoolIdLockString);
+ }
+
+ templateOnPrimary = destPrimaryDataStore.getTemplate(srcTemplateInfo.getId());
if (templateOnPrimary == null) {
- throw new CloudRuntimeException("Failed to create template " + srcTemplateInfo.getUniqueName() + " on primary storage: " + destDataStoreId);
+ templateOnPrimary = createManagedTemplateVolume(srcTemplateInfo, destPrimaryDataStore);
+
+ if (templateOnPrimary == null) {
+ throw new CloudRuntimeException("Failed to create template " + srcTemplateInfo.getUniqueName() + " on primary storage: " + destDataStoreId);
+ }
}
- }
- // Copy the template to the template volume.
- VMTemplateStoragePoolVO templatePoolRef = _tmpltPoolDao.findByPoolTemplate(destPrimaryDataStore.getId(), templateOnPrimary.getId());
+ // Copy the template to the template volume.
+ VMTemplateStoragePoolVO templatePoolRef = _tmpltPoolDao.findByPoolTemplate(destPrimaryDataStore.getId(), templateOnPrimary.getId());
- if (templatePoolRef == null) {
- throw new CloudRuntimeException("Failed to find template " + srcTemplateInfo.getUniqueName() + " in storage pool " + destPrimaryDataStore.getId());
- }
+ if (templatePoolRef == null) {
+ throw new CloudRuntimeException("Failed to find template " + srcTemplateInfo.getUniqueName() + " in storage pool " + destPrimaryDataStore.getId());
+ }
- if (templatePoolRef.getDownloadState() == Status.NOT_DOWNLOADED) {
- copyTemplateToManagedTemplateVolume(srcTemplateInfo, templateOnPrimary, templatePoolRef, destPrimaryDataStore, destHost);
+ if (templatePoolRef.getDownloadState() == Status.NOT_DOWNLOADED) {
+ copyTemplateToManagedTemplateVolume(srcTemplateInfo, templateOnPrimary, templatePoolRef, destPrimaryDataStore, destHost);
+ }
+ } finally {
+ if (lock != null) {
+ lock.unlock();
+ lock.releaseRef();
+ }
}
if (destPrimaryDataStore.getPoolType() != StoragePoolType.PowerFlex) {
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
index 3a34cce..c785354 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
@@ -115,6 +115,7 @@
import com.cloud.storage.template.TemplateLocation;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.Pair;
+import com.cloud.utils.UriUtils;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.script.Script;
import com.cloud.utils.storage.S3.S3Utils;
@@ -1769,8 +1770,14 @@
return new DirectDownloadAnswer(false, msg, true);
}
- s_logger.debug("Checking for free space on the host for downloading the template");
- if (!isEnoughSpaceForDownloadTemplateOnTemporaryLocation(cmd.getTemplateSize())) {
+ Long templateSize = null;
+ if (!org.apache.commons.lang.StringUtils.isBlank(cmd.getUrl())) {
+ String url = cmd.getUrl();
+ templateSize = UriUtils.getRemoteSize(url);
+ }
+
+ s_logger.debug("Checking for free space on the host for downloading the template with physical size: " + templateSize + " and virtual size: " + cmd.getTemplateSize());
+ if (!isEnoughSpaceForDownloadTemplateOnTemporaryLocation(templateSize)) {
String msg = "Not enough space on the defined temporary location to download the template " + cmd.getTemplateId();
s_logger.error(msg);
return new DirectDownloadAnswer(false, msg, true);
diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientConnectionPool.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientConnectionPool.java
new file mode 100644
index 0000000..2daf8e4
--- /dev/null
+++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientConnectionPool.java
@@ -0,0 +1,90 @@
+// 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.datastore.client;
+
+import java.net.URISyntaxException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
+import org.apache.log4j.Logger;
+
+import com.cloud.storage.StorageManager;
+import com.cloud.utils.crypt.DBEncryptionUtil;
+import com.google.common.base.Preconditions;
+
+public class ScaleIOGatewayClientConnectionPool {
+ private static final Logger LOGGER = Logger.getLogger(ScaleIOGatewayClientConnectionPool.class);
+
+ private ConcurrentHashMap<Long, ScaleIOGatewayClient> gatewayClients;
+
+ private static final ScaleIOGatewayClientConnectionPool instance;
+
+ static {
+ instance = new ScaleIOGatewayClientConnectionPool();
+ }
+
+ public static ScaleIOGatewayClientConnectionPool getInstance() {
+ return instance;
+ }
+
+ private ScaleIOGatewayClientConnectionPool() {
+ gatewayClients = new ConcurrentHashMap<Long, ScaleIOGatewayClient>();
+ }
+
+ public ScaleIOGatewayClient getClient(Long storagePoolId, StoragePoolDetailsDao storagePoolDetailsDao)
+ throws NoSuchAlgorithmException, KeyManagementException, URISyntaxException {
+ Preconditions.checkArgument(storagePoolId != null && storagePoolId > 0, "Invalid storage pool id");
+
+ ScaleIOGatewayClient client = null;
+ synchronized (gatewayClients) {
+ client = gatewayClients.get(storagePoolId);
+ if (client == null) {
+ final String url = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_ENDPOINT).getValue();
+ final String encryptedUsername = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_USERNAME).getValue();
+ final String username = DBEncryptionUtil.decrypt(encryptedUsername);
+ final String encryptedPassword = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_PASSWORD).getValue();
+ final String password = DBEncryptionUtil.decrypt(encryptedPassword);
+ final int clientTimeout = StorageManager.STORAGE_POOL_CLIENT_TIMEOUT.valueIn(storagePoolId);
+
+ client = new ScaleIOGatewayClientImpl(url, username, password, false, clientTimeout);
+ gatewayClients.put(storagePoolId, client);
+ LOGGER.debug("Added gateway client for the storage pool: " + storagePoolId);
+ }
+ }
+
+ return client;
+ }
+
+ public boolean removeClient(Long storagePoolId) {
+ Preconditions.checkArgument(storagePoolId != null && storagePoolId > 0, "Invalid storage pool id");
+
+ ScaleIOGatewayClient client = null;
+ synchronized (gatewayClients) {
+ client = gatewayClients.remove(storagePoolId);
+ }
+
+ if (client != null) {
+ LOGGER.debug("Removed gateway client for the storage pool: " + storagePoolId);
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java
index 5b93d4f..c981300 100644
--- a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java
+++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java
@@ -86,6 +86,15 @@
private String password;
private String sessionKey = null;
+ // The session token is valid for 8 hours from the time it was created, unless there has been no activity for 10 minutes
+ // Reference: https://cpsdocs.dellemc.com/bundle/PF_REST_API_RG/page/GUID-92430F19-9F44-42B6-B898-87D5307AE59B.html
+ private static final long MAX_VALID_SESSION_TIME_IN_MILLISECS = 8 * 60 * 60 * 1000; // 8 hrs
+ private static final long MAX_IDLE_TIME_IN_MILLISECS = 10 * 60 * 1000; // 10 mins
+ private static final long BUFFER_TIME_IN_MILLISECS = 30 * 1000; // keep 30 secs buffer before the expiration (to avoid any last-minute operations)
+
+ private long createTime = 0;
+ private long lastUsedTime = 0;
+
public ScaleIOGatewayClientImpl(final String url, final String username, final String password,
final boolean validateCertificate, final int timeout)
throws NoSuchAlgorithmException, KeyManagementException, URISyntaxException {
@@ -117,14 +126,14 @@
this.username = username;
this.password = password;
- authenticate(username, password);
+ authenticate();
}
/////////////////////////////////////////////////////////////
//////////////// Private Helper Methods /////////////////////
/////////////////////////////////////////////////////////////
- private void authenticate(final String username, final String password) {
+ private void authenticate() {
final HttpGet request = new HttpGet(apiURI.toString() + "/login");
request.setHeader(HttpHeaders.AUTHORIZATION, "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes()));
try {
@@ -141,6 +150,24 @@
} catch (final IOException e) {
throw new CloudRuntimeException("Failed to authenticate PowerFlex API Gateway due to:" + e.getMessage());
}
+ long now = System.currentTimeMillis();
+ createTime = lastUsedTime = now;
+ }
+
+ private synchronized void renewClientSessionOnExpiry() {
+ if (isSessionExpired()) {
+ LOG.debug("Session expired, renewing");
+ authenticate();
+ }
+ }
+
+ private boolean isSessionExpired() {
+ long now = System.currentTimeMillis() + BUFFER_TIME_IN_MILLISECS;
+ if ((now - createTime) > MAX_VALID_SESSION_TIME_IN_MILLISECS ||
+ (now - lastUsedTime) > MAX_IDLE_TIME_IN_MILLISECS) {
+ return true;
+ }
+ return false;
}
private void checkAuthFailure(final HttpResponse response) {
@@ -176,9 +203,13 @@
}
private HttpResponse get(final String path) throws IOException {
+ renewClientSessionOnExpiry();
final HttpGet request = new HttpGet(apiURI.toString() + path);
request.setHeader(HttpHeaders.AUTHORIZATION, "Basic " + Base64.getEncoder().encodeToString((this.username + ":" + this.sessionKey).getBytes()));
final HttpResponse response = httpClient.execute(request);
+ synchronized (this) {
+ lastUsedTime = System.currentTimeMillis();
+ }
String responseStatus = (response != null) ? (response.getStatusLine().getStatusCode() + " " + response.getStatusLine().getReasonPhrase()) : "nil";
LOG.debug("GET request path: " + path + ", response: " + responseStatus);
checkAuthFailure(response);
@@ -186,6 +217,7 @@
}
private HttpResponse post(final String path, final Object obj) throws IOException {
+ renewClientSessionOnExpiry();
final HttpPost request = new HttpPost(apiURI.toString() + path);
request.setHeader(HttpHeaders.AUTHORIZATION, "Basic " + Base64.getEncoder().encodeToString((this.username + ":" + this.sessionKey).getBytes()));
request.setHeader("Content-type", "application/json");
@@ -200,6 +232,9 @@
}
}
final HttpResponse response = httpClient.execute(request);
+ synchronized (this) {
+ lastUsedTime = System.currentTimeMillis();
+ }
String responseStatus = (response != null) ? (response.getStatusLine().getStatusCode() + " " + response.getStatusLine().getReasonPhrase()) : "nil";
LOG.debug("POST request path: " + path + ", response: " + responseStatus);
checkAuthFailure(response);
diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/driver/ScaleIOPrimaryDataStoreDriver.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/driver/ScaleIOPrimaryDataStoreDriver.java
index de40535..c2300b9 100644
--- a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/driver/ScaleIOPrimaryDataStoreDriver.java
+++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/driver/ScaleIOPrimaryDataStoreDriver.java
@@ -44,6 +44,7 @@
import org.apache.cloudstack.storage.datastore.api.StoragePoolStatistics;
import org.apache.cloudstack.storage.datastore.api.VolumeStatistics;
import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClient;
+import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClientConnectionPool;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
@@ -73,7 +74,6 @@
import com.cloud.storage.dao.VolumeDao;
import com.cloud.storage.dao.VolumeDetailsDao;
import com.cloud.utils.Pair;
-import com.cloud.utils.crypt.DBEncryptionUtil;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VirtualMachineManager;
import com.google.common.base.Preconditions;
@@ -104,13 +104,7 @@
}
private ScaleIOGatewayClient getScaleIOClient(final Long storagePoolId) throws Exception {
- final int clientTimeout = StorageManager.STORAGE_POOL_CLIENT_TIMEOUT.valueIn(storagePoolId);
- final String url = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_ENDPOINT).getValue();
- final String encryptedUsername = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_USERNAME).getValue();
- final String username = DBEncryptionUtil.decrypt(encryptedUsername);
- final String encryptedPassword = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_PASSWORD).getValue();
- final String password = DBEncryptionUtil.decrypt(encryptedPassword);
- return ScaleIOGatewayClient.getClient(url, username, password, false, clientTimeout);
+ return ScaleIOGatewayClientConnectionPool.getInstance().getClient(storagePoolId, storagePoolDetailsDao);
}
@Override
diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycle.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycle.java
index 023d931..7995906 100644
--- a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycle.java
+++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycle.java
@@ -31,6 +31,8 @@
import javax.inject.Inject;
+import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClientConnectionPool;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
import org.apache.cloudstack.storage.datastore.util.ScaleIOUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope;
@@ -80,6 +82,8 @@
@Inject
private PrimaryDataStoreDao primaryDataStoreDao;
@Inject
+ private StoragePoolDetailsDao storagePoolDetailsDao;
+ @Inject
private StoragePoolHostDao storagePoolHostDao;
@Inject
private PrimaryDataStoreHelper dataStoreHelper;
@@ -232,14 +236,7 @@
List<String> connectedSdcIps = null;
try {
- Map <String, String> dataStoreDetails = primaryDataStoreDao.getDetails(dataStore.getId());
- final String url = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_ENDPOINT);
- final String encryptedUsername = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_USERNAME);
- final String username = DBEncryptionUtil.decrypt(encryptedUsername);
- final String encryptedPassword = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_PASSWORD);
- final String password = DBEncryptionUtil.decrypt(encryptedPassword);
- final int clientTimeout = StorageManager.STORAGE_POOL_CLIENT_TIMEOUT.value();
- ScaleIOGatewayClient client = ScaleIOGatewayClient.getClient(url, username, password, false, clientTimeout);
+ ScaleIOGatewayClient client = ScaleIOGatewayClientConnectionPool.getInstance().getClient(dataStore.getId(), storagePoolDetailsDao);
connectedSdcIps = client.listConnectedSdcIps();
} catch (NoSuchAlgorithmException | KeyManagementException | URISyntaxException e) {
LOGGER.error("Failed to create storage pool", e);
@@ -296,14 +293,7 @@
List<String> connectedSdcIps = null;
try {
- Map <String, String> dataStoreDetails = primaryDataStoreDao.getDetails(dataStore.getId());
- String url = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_ENDPOINT);
- String encryptedUsername = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_USERNAME);
- final String username = DBEncryptionUtil.decrypt(encryptedUsername);
- String encryptedPassword = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_PASSWORD);
- final String password = DBEncryptionUtil.decrypt(encryptedPassword);
- final int clientTimeout = StorageManager.STORAGE_POOL_CLIENT_TIMEOUT.value();
- ScaleIOGatewayClient client = ScaleIOGatewayClient.getClient(url, username, password, false, clientTimeout);
+ ScaleIOGatewayClient client = ScaleIOGatewayClientConnectionPool.getInstance().getClient(dataStore.getId(), storagePoolDetailsDao);
connectedSdcIps = client.listConnectedSdcIps();
} catch (NoSuchAlgorithmException | KeyManagementException | URISyntaxException e) {
LOGGER.error("Failed to create storage pool", e);
@@ -392,6 +382,8 @@
}
}
+ ScaleIOGatewayClientConnectionPool.getInstance().removeClient(dataStore.getId());
+
return dataStoreHelper.deletePrimaryDataStore(dataStore);
}
diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/provider/ScaleIOHostListener.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/provider/ScaleIOHostListener.java
index e27f8bd..f672231 100644
--- a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/provider/ScaleIOHostListener.java
+++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/provider/ScaleIOHostListener.java
@@ -21,14 +21,15 @@
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
-import java.util.Map;
import javax.inject.Inject;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener;
import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClient;
+import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClientConnectionPool;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
import org.apache.log4j.Logger;
import com.cloud.agent.AgentManager;
@@ -39,11 +40,9 @@
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.storage.DataStoreRole;
-import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePool;
import com.cloud.storage.StoragePoolHostVO;
import com.cloud.storage.dao.StoragePoolHostDao;
-import com.cloud.utils.crypt.DBEncryptionUtil;
import com.cloud.utils.exception.CloudRuntimeException;
public class ScaleIOHostListener implements HypervisorHostListener {
@@ -55,6 +54,7 @@
@Inject private HostDao _hostDao;
@Inject private StoragePoolHostDao _storagePoolHostDao;
@Inject private PrimaryDataStoreDao _primaryDataStoreDao;
+ @Inject private StoragePoolDetailsDao _storagePoolDetailsDao;
@Override
public boolean hostAdded(long hostId) {
@@ -90,14 +90,7 @@
private boolean isHostSdcConnected(String hostIpAddress, long poolId) {
try {
- Map<String, String> dataStoreDetails = _primaryDataStoreDao.getDetails(poolId);
- final String url = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_ENDPOINT);
- final String encryptedUsername = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_USERNAME);
- final String username = DBEncryptionUtil.decrypt(encryptedUsername);
- final String encryptedPassword = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_PASSWORD);
- final String password = DBEncryptionUtil.decrypt(encryptedPassword);
- final int clientTimeout = StorageManager.STORAGE_POOL_CLIENT_TIMEOUT.valueIn(poolId);
- ScaleIOGatewayClient client = ScaleIOGatewayClient.getClient(url, username, password, false, clientTimeout);
+ ScaleIOGatewayClient client = ScaleIOGatewayClientConnectionPool.getInstance().getClient(poolId, _storagePoolDetailsDao);
return client.isSdcConnected(hostIpAddress);
} catch (NoSuchAlgorithmException | KeyManagementException | URISyntaxException e) {
s_logger.error("Failed to check host sdc connection", e);
diff --git a/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycleTest.java b/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycleTest.java
index c62371f..eed82ff 100644
--- a/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycleTest.java
+++ b/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycleTest.java
@@ -31,9 +31,7 @@
import static org.mockito.MockitoAnnotations.initMocks;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.UUID;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
@@ -44,8 +42,10 @@
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClient;
+import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClientConnectionPool;
import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClientImpl;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.datastore.provider.ScaleIOHostListener;
import org.apache.cloudstack.storage.datastore.util.ScaleIOUtil;
@@ -80,7 +80,6 @@
import com.cloud.storage.VMTemplateStoragePoolVO;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.template.TemplateManager;
-import com.cloud.utils.crypt.DBEncryptionUtil;
import com.cloud.utils.exception.CloudRuntimeException;
@PrepareForTest(ScaleIOGatewayClient.class)
@@ -90,6 +89,8 @@
@Mock
private PrimaryDataStoreDao primaryDataStoreDao;
@Mock
+ private StoragePoolDetailsDao storagePoolDetailsDao;
+ @Mock
private PrimaryDataStoreHelper dataStoreHelper;
@Mock
private ResourceManager resourceManager;
@@ -135,19 +136,9 @@
final DataStore dataStore = mock(DataStore.class);
when(dataStore.getId()).thenReturn(1L);
- Map <String, String> mockDataStoreDetails = new HashMap<>();
- mockDataStoreDetails.put(ScaleIOGatewayClient.GATEWAY_API_ENDPOINT, "https://192.168.1.19/api");
- String encryptedUsername = DBEncryptionUtil.encrypt("root");
- mockDataStoreDetails.put(ScaleIOGatewayClient.GATEWAY_API_USERNAME, encryptedUsername);
- String encryptedPassword = DBEncryptionUtil.encrypt("Password@123");
- mockDataStoreDetails.put(ScaleIOGatewayClient.GATEWAY_API_PASSWORD, encryptedPassword);
- when(primaryDataStoreDao.getDetails(1L)).thenReturn(mockDataStoreDetails);
-
PowerMockito.mockStatic(ScaleIOGatewayClient.class);
ScaleIOGatewayClientImpl client = mock(ScaleIOGatewayClientImpl.class);
- String username = DBEncryptionUtil.decrypt(encryptedUsername);
- String password = DBEncryptionUtil.decrypt(encryptedPassword);
- when(ScaleIOGatewayClient.getClient("https://192.168.1.19/api", username, password, false, 60)).thenReturn(client);
+ when(ScaleIOGatewayClientConnectionPool.getInstance().getClient(1L, storagePoolDetailsDao)).thenReturn(client);
List<String> connectedSdcIps = new ArrayList<>();
connectedSdcIps.add("192.168.1.1");
diff --git a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
index e219088..a81c39a 100644
--- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
+++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
@@ -3980,13 +3980,18 @@
throw new CloudRuntimeException("Router health checks are not enabled for router " + routerId);
}
- if (cmd.shouldPerformFreshChecks() && !routerService.performRouterHealthChecks(routerId)) {
- throw new CloudRuntimeException("Unable to perform fresh checks on router.");
+ if (cmd.shouldPerformFreshChecks()) {
+ Pair<Boolean, String> healthChecksresult = routerService.performRouterHealthChecks(routerId);
+ if (healthChecksresult == null) {
+ throw new CloudRuntimeException("Failed to initiate fresh checks on router.");
+ } else if (!healthChecksresult.first()) {
+ throw new CloudRuntimeException("Unable to perform fresh checks on router - " + healthChecksresult.second());
+ }
}
List<RouterHealthCheckResult> result = new ArrayList<>(routerHealthCheckResultDao.getHealthCheckResults(routerId));
if (result == null || result.size() == 0) {
- throw new CloudRuntimeException("Database had no entries for health checks for router. This could happen for " +
+ throw new CloudRuntimeException("No health check results found for the router. This could happen for " +
"a newly created router. Please wait for periodic results to populate or manually call for checks to execute.");
}
diff --git a/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java b/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java
index 39d902f..29315c8 100644
--- a/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java
+++ b/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java
@@ -71,6 +71,7 @@
import com.cloud.network.addr.PublicIp;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.NetworkDao;
+import com.cloud.network.dao.RouterHealthCheckResultDao;
import com.cloud.network.dao.UserIpv6AddressDao;
import com.cloud.network.lb.LoadBalancingRule;
import com.cloud.network.router.VirtualRouter.RedundantState;
@@ -156,6 +157,8 @@
ConfigurationDao _configDao;
@Inject
VpcVirtualNetworkApplianceManager _vpcRouterMgr;
+ @Inject
+ RouterHealthCheckResultDao _routerHealthCheckResultDao;
protected final Map<HypervisorType, ConfigKey<String>> hypervisorsMap = new HashMap<>();
@@ -254,6 +257,7 @@
_accountMgr.checkAccess(caller, null, true, router);
_itMgr.expunge(router.getUuid());
+ _routerHealthCheckResultDao.expungeHealthChecks(router.getId());
_routerDao.remove(router.getId());
return router;
}
diff --git a/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
index df0d4c1..0925048 100644
--- a/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
@@ -1268,68 +1268,68 @@
ex.printStackTrace();
}
}
+ }
- private List<String> getFailingChecks(DomainRouterVO router, GetRouterMonitorResultsAnswer answer) {
+ private List<String> getFailingChecks(DomainRouterVO router, GetRouterMonitorResultsAnswer answer) {
- if (answer == null) {
- s_logger.warn("Unable to fetch monitor results for router " + router);
- resetRouterHealthChecksAndConnectivity(router.getId(), false, false, "Communication failed");
- return Arrays.asList(CONNECTIVITY_TEST);
- } else if (!answer.getResult()) {
- s_logger.warn("Failed to fetch monitor results from router " + router + " with details: " + answer.getDetails());
- if (StringUtils.isNotBlank(answer.getDetails()) && answer.getDetails().equalsIgnoreCase(READONLY_FILESYSTEM_ERROR)) {
- resetRouterHealthChecksAndConnectivity(router.getId(), true, false, "Failed to write: " + answer.getDetails());
- return Arrays.asList(FILESYSTEM_WRITABLE_TEST);
- } else {
- resetRouterHealthChecksAndConnectivity(router.getId(), false, false, "Failed to fetch results with details: " + answer.getDetails());
- return Arrays.asList(CONNECTIVITY_TEST);
- }
+ if (answer == null) {
+ s_logger.warn("Unable to fetch monitor results for router " + router);
+ resetRouterHealthChecksAndConnectivity(router.getId(), false, false, "Communication failed");
+ return Arrays.asList(CONNECTIVITY_TEST);
+ } else if (!answer.getResult()) {
+ s_logger.warn("Failed to fetch monitor results from router " + router + " with details: " + answer.getDetails());
+ if (StringUtils.isNotBlank(answer.getDetails()) && answer.getDetails().equalsIgnoreCase(READONLY_FILESYSTEM_ERROR)) {
+ resetRouterHealthChecksAndConnectivity(router.getId(), true, false, "Failed to write: " + answer.getDetails());
+ return Arrays.asList(FILESYSTEM_WRITABLE_TEST);
} else {
- resetRouterHealthChecksAndConnectivity(router.getId(), true, true, "Successfully fetched data");
- updateDbHealthChecksFromRouterResponse(router.getId(), answer.getMonitoringResults());
- return answer.getFailingChecks();
+ resetRouterHealthChecksAndConnectivity(router.getId(), false, false, "Failed to fetch results with details: " + answer.getDetails());
+ return Arrays.asList(CONNECTIVITY_TEST);
+ }
+ } else {
+ resetRouterHealthChecksAndConnectivity(router.getId(), true, true, "Successfully fetched data");
+ updateDbHealthChecksFromRouterResponse(router.getId(), answer.getMonitoringResults());
+ return answer.getFailingChecks();
+ }
+ }
+
+ private void handleFailingChecks(DomainRouterVO router, List<String> failingChecks) {
+ if (failingChecks == null || failingChecks.size() == 0) {
+ return;
+ }
+
+ String alertMessage = "Health checks failed: " + failingChecks.size() + " failing checks on router " + router.getUuid();
+ _alertMgr.sendAlert(AlertType.ALERT_TYPE_DOMAIN_ROUTER, router.getDataCenterId(), router.getPodIdToDeployIn(),
+ alertMessage, alertMessage);
+ s_logger.warn(alertMessage + ". Checking failed health checks to see if router needs recreate");
+
+ String checkFailsToRecreateVr = RouterHealthChecksFailuresToRecreateVr.valueIn(router.getDataCenterId());
+ StringBuilder failingChecksEvent = new StringBuilder();
+ boolean recreateRouter = false;
+ for (int i = 0; i < failingChecks.size(); i++) {
+ String failedCheck = failingChecks.get(i);
+ if (i == 0) {
+ failingChecksEvent.append("Router ")
+ .append(router.getUuid())
+ .append(" has failing checks: ");
+ }
+
+ failingChecksEvent.append(failedCheck);
+ if (i < failingChecks.size() - 1) {
+ failingChecksEvent.append(", ");
+ }
+
+ if (StringUtils.isNotBlank(checkFailsToRecreateVr) && checkFailsToRecreateVr.contains(failedCheck)) {
+ recreateRouter = true;
}
}
- private void handleFailingChecks(DomainRouterVO router, List<String> failingChecks) {
- if (failingChecks == null || failingChecks.size() == 0) {
- return;
- }
+ ActionEventUtils.onActionEvent(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM,
+ Domain.ROOT_DOMAIN, EventTypes.EVENT_ROUTER_HEALTH_CHECKS, failingChecksEvent.toString());
- String alertMessage = "Health checks failed: " + failingChecks.size() + " failing checks on router " + router.getUuid();
- _alertMgr.sendAlert(AlertType.ALERT_TYPE_DOMAIN_ROUTER, router.getDataCenterId(), router.getPodIdToDeployIn(),
- alertMessage, alertMessage);
- s_logger.warn(alertMessage + ". Checking failed health checks to see if router needs recreate");
-
- String checkFailsToRecreateVr = RouterHealthChecksFailuresToRecreateVr.valueIn(router.getDataCenterId());
- StringBuilder failingChecksEvent = new StringBuilder();
- boolean recreateRouter = false;
- for (int i = 0; i < failingChecks.size(); i++) {
- String failedCheck = failingChecks.get(i);
- if (i == 0) {
- failingChecksEvent.append("Router ")
- .append(router.getUuid())
- .append(" has failing checks: ");
- }
-
- failingChecksEvent.append(failedCheck);
- if (i < failingChecks.size() - 1) {
- failingChecksEvent.append(", ");
- }
-
- if (StringUtils.isNotBlank(checkFailsToRecreateVr) && checkFailsToRecreateVr.contains(failedCheck)) {
- recreateRouter = true;
- }
- }
-
- ActionEventUtils.onActionEvent(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM,
- Domain.ROOT_DOMAIN, EventTypes.EVENT_ROUTER_HEALTH_CHECKS, failingChecksEvent.toString());
-
- if (recreateRouter) {
- s_logger.warn("Health Check Alert: Found failing checks in " +
- RouterHealthChecksFailuresToRecreateVrCK + ", attempting recreating router.");
- recreateRouter(router.getId());
- }
+ if (recreateRouter) {
+ s_logger.warn("Health Check Alert: Found failing checks in " +
+ RouterHealthChecksFailuresToRecreateVrCK + ", attempting recreating router.");
+ recreateRouter(router.getId());
}
}
@@ -1551,7 +1551,7 @@
String controlIP = getRouterControlIP(router);
if (StringUtils.isNotBlank(controlIP) && !controlIP.equals("0.0.0.0")) {
- final GetRouterMonitorResultsCommand command = new GetRouterMonitorResultsCommand(performFreshChecks);
+ final GetRouterMonitorResultsCommand command = new GetRouterMonitorResultsCommand(performFreshChecks, false);
command.setAccessDetail(NetworkElementCommand.ROUTER_IP, controlIP);
command.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
try {
@@ -1576,8 +1576,40 @@
return null;
}
+ private GetRouterMonitorResultsAnswer performBasicTestsOnRouter(DomainRouterVO router) {
+ if (!RouterHealthChecksEnabled.value()) {
+ return null;
+ }
+
+ String controlIP = getRouterControlIP(router);
+ if (StringUtils.isNotBlank(controlIP) && !controlIP.equals("0.0.0.0")) {
+ final GetRouterMonitorResultsCommand command = new GetRouterMonitorResultsCommand(false, true);
+ command.setAccessDetail(NetworkElementCommand.ROUTER_IP, controlIP);
+ command.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
+ try {
+ final Answer answer = _agentMgr.easySend(router.getHostId(), command);
+
+ if (answer == null) {
+ s_logger.warn("Unable to fetch basic router test results data from router " + router.getHostName());
+ return null;
+ }
+ if (answer instanceof GetRouterMonitorResultsAnswer) {
+ return (GetRouterMonitorResultsAnswer) answer;
+ } else {
+ s_logger.warn("Unable to fetch basic router test results from router " + router.getHostName() + " Received answer " + answer.getDetails());
+ return new GetRouterMonitorResultsAnswer(command, false, null, answer.getDetails());
+ }
+ } catch (final Exception e) {
+ s_logger.warn("Error while performing basic tests on router: " + router.getInstanceName(), e);
+ return null;
+ }
+ }
+
+ return null;
+ }
+
@Override
- public boolean performRouterHealthChecks(long routerId) {
+ public Pair<Boolean, String> performRouterHealthChecks(long routerId) {
DomainRouterVO router = _routerDao.findById(routerId);
if (router == null) {
@@ -1590,35 +1622,45 @@
s_logger.info("Running health check results for router " + router.getUuid());
- final GetRouterMonitorResultsAnswer answer;
+ GetRouterMonitorResultsAnswer answer = null;
+ String resultDetails = "";
boolean success = true;
- // Step 1: Update health check data on router and perform and retrieve health checks on router
- if (!updateRouterHealthChecksConfig(router)) {
- s_logger.warn("Unable to update health check config for fresh run successfully for router: " + router + ", so trying to fetch last result.");
- success = false;
- answer = fetchAndUpdateRouterHealthChecks(router, false);
- } else {
- s_logger.info("Successfully updated health check config for fresh run successfully for router: " + router);
- answer = fetchAndUpdateRouterHealthChecks(router, true);
- }
- // Step 2: Update health checks values in database. We do this irrespective of new health check config.
+ // Step 1: Perform basic tests to check the connectivity and file system on router
+ answer = performBasicTestsOnRouter(router);
if (answer == null) {
+ s_logger.debug("No results received for the basic tests on router: " + router);
+ resultDetails = "Basic tests results unavailable";
success = false;
- resetRouterHealthChecksAndConnectivity(routerId, false, false, "Communication failed");
} else if (!answer.getResult()) {
+ s_logger.debug("Basic tests failed on router: " + router);
+ resultDetails = "Basic tests failed - " + answer.getMonitoringResults();
success = false;
- if (StringUtils.isNotBlank(answer.getDetails()) && answer.getDetails().equalsIgnoreCase(READONLY_FILESYSTEM_ERROR)) {
- resetRouterHealthChecksAndConnectivity(routerId, true, false, "Failed to write: " + answer.getDetails());
- } else {
- resetRouterHealthChecksAndConnectivity(routerId, false, false, "Failed to fetch results with details: " + answer.getDetails());
- }
} else {
- resetRouterHealthChecksAndConnectivity(routerId, true, true, "Successfully fetched data");
- updateDbHealthChecksFromRouterResponse(routerId, answer.getMonitoringResults());
+ // Step 2: Update health check data on router and perform and retrieve health checks on router
+ if (!updateRouterHealthChecksConfig(router)) {
+ s_logger.warn("Unable to update health check config for fresh run successfully for router: " + router + ", so trying to fetch last result.");
+ success = false;
+ answer = fetchAndUpdateRouterHealthChecks(router, false);
+ } else {
+ s_logger.info("Successfully updated health check config for fresh run successfully for router: " + router);
+ answer = fetchAndUpdateRouterHealthChecks(router, true);
+ }
+
+ if (answer == null) {
+ resultDetails = "Failed to fetch and update health checks";
+ success = false;
+ } else if (!answer.getResult()) {
+ resultDetails = "Get health checks failed - " + answer.getMonitoringResults();
+ success = false;
+ }
}
- return success;
+ // Step 3: Update health checks values in database. We do this irrespective of new health check config.
+ List<String> failingChecks = getFailingChecks(router, answer);
+ handleFailingChecks(router, failingChecks);
+
+ return new Pair<Boolean, String>(success, resultDetails);
}
protected class UpdateRouterHealthChecksConfigTask extends ManagedContextRunnable {
@@ -1632,7 +1674,13 @@
s_logger.debug("Found " + routers.size() + " running routers. ");
for (final DomainRouterVO router : routers) {
- updateRouterHealthChecksConfig(router);
+ GetRouterMonitorResultsAnswer answer = performBasicTestsOnRouter(router);
+ if (answer != null && answer.getResult()) {
+ updateRouterHealthChecksConfig(router);
+ } else {
+ String resultDetails = (answer == null) ? "" : ", " + answer.getMonitoringResults();
+ s_logger.debug("Couldn't update health checks config on router: " + router + " as basic tests didn't succeed" + resultDetails);
+ }
}
} catch (final Exception ex) {
s_logger.error("Fail to complete the UpdateRouterHealthChecksConfigTask! ", ex);
@@ -1665,7 +1713,6 @@
return false;
}
- SetMonitorServiceCommand command = createMonitorServiceCommand(router, null,true, true);
String controlIP = getRouterControlIP(router);
if (StringUtils.isBlank(controlIP) || controlIP.equals("0.0.0.0")) {
s_logger.debug("Skipping update data on router " + router.getUuid() + " because controlIp is not correct.");
@@ -1675,6 +1722,7 @@
s_logger.info("Updating data for router health checks for router " + router.getUuid());
Answer origAnswer = null;
try {
+ SetMonitorServiceCommand command = createMonitorServiceCommand(router, null, true, true);
origAnswer = _agentMgr.easySend(router.getHostId(), command);
} catch (final Exception e) {
s_logger.error("Error while sending update data for health check to router: " + router.getInstanceName(), e);
diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
index 33ca12a..d1ddb52 100644
--- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
+++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
@@ -1292,7 +1292,7 @@
return volume;
} catch (Exception e) {
- throw new CloudRuntimeException("Exception caught during resize volume operation of volume UUID: " + volume.getUuid(), e);
+ throw new CloudRuntimeException("Couldn't resize volume: " + volume.getName() + ", " + e.getMessage(), e);
}
}
@@ -2676,6 +2676,10 @@
throw new InvalidParameterValueException("VolumeId: " + volumeId + " please attach this volume to a VM before create snapshot for it");
}
+ if (storagePool.getPoolType() == Storage.StoragePoolType.PowerFlex) {
+ throw new InvalidParameterValueException("Cannot perform this operation, unsupported on storage pool type " + storagePool.getPoolType());
+ }
+
return snapshotMgr.allocSnapshot(volumeId, Snapshot.MANUAL_POLICY_ID, snapshotName, null);
}
@@ -2706,7 +2710,13 @@
}
if (volume.getPoolId() == null) {
throw new InvalidParameterValueException("The volume doesn't belong to a storage pool so can't extract it");
+ } else {
+ StoragePoolVO poolVO = _storagePoolDao.findById(volume.getPoolId());
+ if (poolVO != null && poolVO.getPoolType() == Storage.StoragePoolType.PowerFlex) {
+ throw new InvalidParameterValueException("Cannot extract volume, this operation is unsupported for volumes on storage pool type " + poolVO.getPoolType());
+ }
}
+
// Extract activity only for detached volumes or for volumes whose
// instance is stopped
if (volume.getInstanceId() != null && ApiDBUtils.findVMInstanceById(volume.getInstanceId()).getState() != State.Stopped) {
diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
index 1479f8d..106709f 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
@@ -4830,6 +4830,8 @@
destinationHost = _hostDao.findById(hostId);
if (destinationHost == null) {
throw new InvalidParameterValueException("Unable to find the host to deploy the VM, host id=" + hostId);
+ } else if (destinationHost.getResourceState() != ResourceState.Enabled || destinationHost.getStatus() != Status.Up ) {
+ throw new InvalidParameterValueException("Unable to deploy the VM as the host: " + destinationHost.getName() + " is not in the right state");
}
}
return destinationHost;
@@ -5097,8 +5099,6 @@
throw new InvalidParameterValueException("Unable to find service offering: " + serviceOfferingId);
}
- Long templateId = cmd.getTemplateId();
-
if (!serviceOffering.isDynamic()) {
for(String detail: cmd.getDetails().keySet()) {
if(detail.equalsIgnoreCase(VmDetailConstants.CPU_NUMBER) || detail.equalsIgnoreCase(VmDetailConstants.CPU_SPEED) || detail.equalsIgnoreCase(VmDetailConstants.MEMORY)) {
@@ -5107,6 +5107,8 @@
}
}
+ Long templateId = cmd.getTemplateId();
+
VirtualMachineTemplate template = _entityMgr.findById(VirtualMachineTemplate.class, templateId);
// Make sure a valid template ID was specified
if (template == null) {
@@ -5131,6 +5133,14 @@
}
}
+ Account caller = CallContext.current().getCallingAccount();
+ Long callerId = caller.getId();
+
+ boolean isRootAdmin = _accountService.isRootAdmin(callerId);
+
+ Long hostId = cmd.getHostId();
+ getDestinationHost(hostId, isRootAdmin);
+
String ipAddress = cmd.getIpAddress();
String ip6Address = cmd.getIp6Address();
String macAddress = cmd.getMacAddress();
@@ -5181,8 +5191,6 @@
}
// Add extraConfig to user_vm_details table
- Account caller = CallContext.current().getCallingAccount();
- Long callerId = caller.getId();
String extraConfig = cmd.getExtraConfig();
if (StringUtils.isNotBlank(extraConfig)) {
if (EnableAdditionalVmConfig.valueIn(callerId)) {
diff --git a/server/src/test/java/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java b/server/src/test/java/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java
index 45bf4c1..abb1863 100644
--- a/server/src/test/java/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java
+++ b/server/src/test/java/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java
@@ -39,6 +39,7 @@
import com.cloud.network.vpc.PrivateGateway;
import com.cloud.user.Account;
import com.cloud.user.User;
+import com.cloud.utils.Pair;
import com.cloud.utils.component.ManagerBase;
import com.cloud.vm.DomainRouterVO;
import com.cloud.vm.VirtualMachineProfile;
@@ -249,8 +250,8 @@
}
@Override
- public boolean performRouterHealthChecks(long routerId) {
- return false;
+ public Pair<Boolean, String> performRouterHealthChecks(long routerId) {
+ return null;
}
@Override