This is an automated email from the ASF dual-hosted git repository.
rohit pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/main by this push:
new 07cabbe scaleio: Updated PowerFlex/ScaleIO gateway client with some
improvements. (#5037)
07cabbe is described below
commit 07cabbe7ac4379a69591db88e3ad4f0b7f0d095f
Author: sureshanaparti <[email protected]>
AuthorDate: Wed Jun 16 12:45:27 2021 +0530
scaleio: Updated PowerFlex/ScaleIO gateway client with some improvements.
(#5037)
- Added connection manager to the gateway client.
- Renew the client session on '401 Unauthorized' response.
- Refactored the gateway client calls, for GET and POST methods.
- Consume the http entity content after login/(re)authentication and close
the content stream if exists.
- Updated storage pool client connection timeout configuration
'storage.pool.client.timeout' to non-dynamic.
- Added storage pool client max connections configuration
'storage.pool.client.max.connections' (default: 100) to specify the maximum
connections for the ScaleIO storage pool client.
- Updated unit tests.
and blocked the attach volume operation for uploaded volume on
ScaleIO/PowerFlex storage pool
---
.../java/com/cloud/storage/StorageManager.java | 11 +-
plugins/storage/volume/scaleio/pom.xml | 6 +
.../datastore/client/ScaleIOGatewayClient.java | 4 +-
.../client/ScaleIOGatewayClientConnectionPool.java | 3 +-
.../datastore/client/ScaleIOGatewayClientImpl.java | 981 +++++++++------------
.../ScaleIOPrimaryDataStoreLifeCycle.java | 7 +-
.../client/ScaleIOGatewayClientImplTest.java | 157 +++-
.../configuration/ConfigurationManagerImpl.java | 3 +
.../java/com/cloud/storage/StorageManagerImpl.java | 1 +
.../com/cloud/storage/VolumeApiServiceImpl.java | 11 +
10 files changed, 600 insertions(+), 584 deletions(-)
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 db41a2f..96cf333 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
@@ -124,7 +124,16 @@ public interface StorageManager extends StorageService {
"Storage",
"60",
"Timeout (in secs) for the storage pool client connection timeout
(for managed pools). Currently only supported for PowerFlex.",
- true,
+ false,
+ ConfigKey.Scope.StoragePool,
+ null);
+
+ ConfigKey<Integer> STORAGE_POOL_CLIENT_MAX_CONNECTIONS = new
ConfigKey<>(Integer.class,
+ "storage.pool.client.max.connections",
+ "Storage",
+ "100",
+ "Maximum connections for the storage pool client (for managed
pools). Currently only supported for PowerFlex.",
+ false,
ConfigKey.Scope.StoragePool,
null);
diff --git a/plugins/storage/volume/scaleio/pom.xml
b/plugins/storage/volume/scaleio/pom.xml
index e95087e..36a385c 100644
--- a/plugins/storage/volume/scaleio/pom.xml
+++ b/plugins/storage/volume/scaleio/pom.xml
@@ -33,6 +33,12 @@
<artifactId>cloud-engine-storage-volume</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>com.github.tomakehurst</groupId>
+ <artifactId>wiremock-standalone</artifactId>
+ <version>${cs.wiremock.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
<plugins>
diff --git
a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClient.java
b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClient.java
index f6b10f8..f497b10 100644
---
a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClient.java
+++
b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClient.java
@@ -40,8 +40,8 @@ public interface ScaleIOGatewayClient {
String STORAGE_POOL_SYSTEM_ID = "powerflex.storagepool.system.id";
static ScaleIOGatewayClient getClient(final String url, final String
username, final String password,
- final boolean validateCertificate,
final int timeout) throws NoSuchAlgorithmException, KeyManagementException,
URISyntaxException {
- return new ScaleIOGatewayClientImpl(url, username, password,
validateCertificate, timeout);
+ final boolean validateCertificate,
final int timeout, final int maxConnections) throws NoSuchAlgorithmException,
KeyManagementException, URISyntaxException {
+ return new ScaleIOGatewayClientImpl(url, username, password,
validateCertificate, timeout, maxConnections);
}
// Volume APIs
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
index 2daf8e4..e557e08 100644
---
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
@@ -62,8 +62,9 @@ public class ScaleIOGatewayClientConnectionPool {
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);
+ final int clientMaxConnections =
StorageManager.STORAGE_POOL_CLIENT_MAX_CONNECTIONS.valueIn(storagePoolId);
- client = new ScaleIOGatewayClientImpl(url, username, password,
false, clientTimeout);
+ client = new ScaleIOGatewayClientImpl(url, username, password,
false, clientTimeout, clientMaxConnections);
gatewayClients.put(storagePoolId, client);
LOGGER.debug("Added gateway client for the storage pool: " +
storagePoolId);
}
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 5e8568d..fa19541 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
@@ -56,11 +56,16 @@ import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.pool.PoolStats;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
@@ -80,25 +85,27 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
private final URI apiURI;
private final HttpClient httpClient;
- private static final String SESSION_HEADER = "X-RestSvcSessionId";
+ private final PoolingHttpClientConnectionManager connectionManager;
private static final String MDM_CONNECTED_STATE = "Connected";
- private String host;
private String username;
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 MAX_VALID_SESSION_TIME_IN_HRS = 8;
+ private static final long MAX_VALID_SESSION_TIME_IN_MILLISECS =
MAX_VALID_SESSION_TIME_IN_HRS * 60 * 60 * 1000;
+ private static final long MAX_IDLE_TIME_IN_MINS = 10;
+ private static final long MAX_IDLE_TIME_IN_MILLISECS =
MAX_IDLE_TIME_IN_MINS * 60 * 1000;
private static final long BUFFER_TIME_IN_MILLISECS = 30 * 1000; // keep 30
secs buffer before the expiration (to avoid any last-minute operations)
+ private boolean authenticating = false;
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)
+ final boolean validateCertificate, final
int timeout, final int maxConnections)
throws NoSuchAlgorithmException, KeyManagementException,
URISyntaxException {
Preconditions.checkArgument(!Strings.isNullOrEmpty(url), "Gateway
client url cannot be null");
Preconditions.checkArgument(!Strings.isNullOrEmpty(username) &&
!Strings.isNullOrEmpty(password), "Gateway client credentials cannot be null");
@@ -109,83 +116,141 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
.setSocketTimeout(timeout * 1000)
.build();
+ SSLConnectionSocketFactory factory =
SSLConnectionSocketFactory.getSocketFactory();
if (!validateCertificate) {
final SSLContext sslcontext = SSLUtils.getSSLContext();
sslcontext.init(null, new X509TrustManager[]{new
TrustAllManager()}, new SecureRandom());
- final SSLConnectionSocketFactory factory = new
SSLConnectionSocketFactory(sslcontext, NoopHostnameVerifier.INSTANCE);
- this.httpClient = HttpClientBuilder.create()
- .setDefaultRequestConfig(config)
- .setSSLSocketFactory(factory)
- .build();
- } else {
- this.httpClient = HttpClientBuilder.create()
- .setDefaultRequestConfig(config)
- .build();
+ factory = new SSLConnectionSocketFactory(sslcontext,
NoopHostnameVerifier.INSTANCE);
}
+ final Registry<ConnectionSocketFactory> socketFactoryRegistry =
RegistryBuilder.<ConnectionSocketFactory> create()
+ .register("https", factory)
+ .build();
+ connectionManager = new
PoolingHttpClientConnectionManager(socketFactoryRegistry);
+ connectionManager.setMaxTotal(maxConnections);
+ connectionManager.setDefaultMaxPerRoute(maxConnections);
+
+ this.httpClient = HttpClientBuilder.create()
+ .setConnectionManager(connectionManager)
+ .setDefaultRequestConfig(config)
+ .setSSLSocketFactory(factory)
+ .build();
+
this.apiURI = new URI(url);
- this.host = apiURI.getHost();
this.username = username;
this.password = password;
authenticate();
+ LOG.debug("API client for the PowerFlex gateway " + apiURI.getHost() +
" is created successfully, with max connections: "
+ + maxConnections + " and timeout: " + timeout + " secs");
}
/////////////////////////////////////////////////////////////
//////////////// Private Helper Methods /////////////////////
/////////////////////////////////////////////////////////////
- private void authenticate() {
+ private synchronized void authenticate() {
final HttpGet request = new HttpGet(apiURI.toString() + "/login");
request.setHeader(HttpHeaders.AUTHORIZATION, "Basic " +
Base64.getEncoder().encodeToString((username + ":" + password).getBytes()));
+ HttpResponse response = null;
try {
- final HttpResponse response = httpClient.execute(request);
- checkAuthFailure(response);
- this.sessionKey = EntityUtils.toString(response.getEntity());
- if (Strings.isNullOrEmpty(this.sessionKey)) {
- throw new CloudRuntimeException("Failed to create a valid
PowerFlex Gateway Session to perform API requests");
+ authenticating = true;
+ LOG.debug("Authenticating gateway " + apiURI.getHost() + " with
the request: " + request.toString());
+ response = httpClient.execute(request);
+ if (isNullResponse(response)) {
+ LOG.warn("Invalid response received while authenticating, for
the request: " + request.toString());
+ throw new CloudRuntimeException("Failed to authenticate
PowerFlex API Gateway due to invalid response from the Gateway " +
apiURI.getHost());
}
- this.sessionKey = this.sessionKey.replace("\"", "");
+
+ LOG.debug("Received response: " +
response.getStatusLine().getStatusCode() + " " +
response.getStatusLine().getReasonPhrase()
+ + ", for the authenticate request: " + request.toString());
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
- throw new CloudRuntimeException("PowerFlex Gateway login
failed, please check the provided settings");
+ throw new CloudRuntimeException("PowerFlex Gateway " +
apiURI.getHost() + " login failed, please check the provided settings");
}
+
+ String sessionKeyInResponse =
EntityUtils.toString(response.getEntity());
+ if (Strings.isNullOrEmpty(sessionKeyInResponse)) {
+ throw new CloudRuntimeException("Failed to create a valid
session for PowerFlex Gateway " + apiURI.getHost() + " to perform API
requests");
+ }
+
+ LOG.info("PowerFlex API Gateway " + apiURI.getHost() + "
authenticated successfully");
+ this.sessionKey = sessionKeyInResponse.replace("\"", "");
+
+ long now = System.currentTimeMillis();
+ createTime = lastUsedTime = now;
} catch (final IOException e) {
- throw new CloudRuntimeException("Failed to authenticate PowerFlex
API Gateway due to:" + e.getMessage());
+ LOG.error("Failed to authenticate PowerFlex API Gateway " +
apiURI.getHost() + " due to: " + e.getMessage() + getConnectionManagerStats());
+ throw new CloudRuntimeException("Failed to authenticate PowerFlex
API Gateway " + apiURI.getHost() + " due to: " + e.getMessage());
+ } finally {
+ authenticating = false;
+ if (response != null) {
+ EntityUtils.consumeQuietly(response.getEntity());
+ }
}
- long now = System.currentTimeMillis();
- createTime = lastUsedTime = now;
}
private synchronized void renewClientSessionOnExpiry() {
if (isSessionExpired()) {
- LOG.debug("Session expired, renewing");
+ LOG.debug("Session expired for the PowerFlex API Gateway " +
apiURI.getHost() + ", 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) {
+ if ((now - createTime) > MAX_VALID_SESSION_TIME_IN_MILLISECS) {
+ LOG.debug("Session expired for the Gateway " + apiURI.getHost() +
", token is invalid after " + MAX_VALID_SESSION_TIME_IN_HRS
+ + " hours from the time it was created");
+ return true;
+ }
+
+ if ((now - lastUsedTime) > MAX_IDLE_TIME_IN_MILLISECS) {
+ LOG.debug("Session expired for the Gateway " + apiURI.getHost() +
", as there has been no activity for " + MAX_IDLE_TIME_IN_MINS + " mins");
return true;
}
+
return false;
}
- private void checkAuthFailure(final HttpResponse response) {
- if (response != null && response.getStatusLine().getStatusCode() ==
HttpStatus.SC_UNAUTHORIZED) {
- throw new ServerApiException(ApiErrorCode.UNAUTHORIZED, "PowerFlex
Gateway API call unauthorized, please check the provided settings");
+ private boolean isNullResponse(final HttpResponse response) {
+ if (response == null) {
+ LOG.warn("Nil response");
+ return true;
+ }
+
+ if (response.getStatusLine() == null) {
+ LOG.warn("No status line in the response");
+ return true;
}
+
+ return false;
+ }
+
+ private boolean checkAuthFailure(final HttpResponse response, final
boolean renewAndRetryOnAuthFailure) {
+ if (!isNullResponse(response) &&
response.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
+ if (!renewAndRetryOnAuthFailure) {
+ throw new ServerApiException(ApiErrorCode.UNAUTHORIZED,
"PowerFlex Gateway API call unauthorized, please check the provided settings");
+ }
+ LOG.debug("PowerFlex Gateway API call unauthorized. Current token
might be invalid, renew the session." + getConnectionManagerStats());
+ return true;
+ }
+ return false;
}
private void checkResponseOK(final HttpResponse response) {
+ if (isNullResponse(response)) {
+ return;
+ }
+
if (response.getStatusLine().getStatusCode() ==
HttpStatus.SC_NO_CONTENT) {
- LOG.debug("Requested resource does not exist");
+ LOG.warn("Requested resource does not exist");
return;
}
+
if (response.getStatusLine().getStatusCode() ==
HttpStatus.SC_BAD_REQUEST) {
throw new
ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Bad API request");
}
+
if (!(response.getStatusLine().getStatusCode() == HttpStatus.SC_OK ||
response.getStatusLine().getStatusCode() ==
HttpStatus.SC_ACCEPTED)) {
String responseBody = response.toString();
@@ -193,54 +258,118 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
responseBody = EntityUtils.toString(response.getEntity());
} catch (IOException ignored) {
}
- LOG.debug("HTTP request failed, status code is " +
response.getStatusLine().getStatusCode() + ", response is: " + responseBody);
+ LOG.debug("HTTP request failed, status code: " +
response.getStatusLine().getStatusCode() + ", response: "
+ + responseBody + getConnectionManagerStats());
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "API
failed due to: " + responseBody);
}
}
private void checkResponseTimeOut(final Exception e) {
if (e instanceof ConnectTimeoutException || e instanceof
SocketTimeoutException) {
- throw new
ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, "API operation
timed out, please try again.");
+ throw new
ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, "Gateway API
operation timed out, please try again.");
}
}
- private HttpResponse get(final String path) throws IOException {
+ private <T> T get(final String path, final Class<T> type) {
+ return get(path, type, true);
+ }
+
+ private <T> T get(final String path, final Class<T> type, final boolean
renewAndRetryOnAuthFailure) {
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();
+ HttpResponse response = null;
+ boolean responseConsumed = false;
+ try {
+ while (authenticating); // wait for authentication request (if
any) to complete (and to pick the new session key)
+ final HttpGet request = new HttpGet(apiURI.toString() + path);
+ request.setHeader(HttpHeaders.AUTHORIZATION, "Basic " +
Base64.getEncoder().encodeToString((this.username + ":" +
this.sessionKey).getBytes()));
+ LOG.debug("Sending GET request: " + request.toString());
+ response = httpClient.execute(request);
+ String responseStatus = (!isNullResponse(response)) ?
(response.getStatusLine().getStatusCode() + " " +
response.getStatusLine().getReasonPhrase()) : "nil";
+ LOG.debug("Received response: " + responseStatus + ", for the sent
GET request: " + request.toString());
+ if (checkAuthFailure(response, renewAndRetryOnAuthFailure)) {
+ EntityUtils.consumeQuietly(response.getEntity());
+ responseConsumed = true;
+
+ authenticate();
+ return get(path, type, false);
+ }
+ return processResponse(response, type);
+ } catch (final IOException e) {
+ LOG.error("Failed in GET method due to: " + e.getMessage() +
getConnectionManagerStats(), e);
+ checkResponseTimeOut(e);
+ } finally {
+ if (!responseConsumed && response != null) {
+ EntityUtils.consumeQuietly(response.getEntity());
+ }
}
- String responseStatus = (response != null) ?
(response.getStatusLine().getStatusCode() + " " +
response.getStatusLine().getReasonPhrase()) : "nil";
- LOG.debug("GET request path: " + path + ", response: " +
responseStatus);
- checkAuthFailure(response);
- return response;
+ return null;
}
- private HttpResponse post(final String path, final Object obj) throws
IOException {
+ private <T> T post(final String path, final Object obj, final Class<T>
type) {
+ return post(path, obj, type, true);
+ }
+
+ private <T> T post(final String path, final Object obj, final Class<T>
type, final boolean renewAndRetryOnAuthFailure) {
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");
- if (obj != null) {
- if (obj instanceof String) {
- request.setEntity(new StringEntity((String) obj));
- } else {
- JsonMapper mapper = new JsonMapper();
- mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
- String json = mapper.writer().writeValueAsString(obj);
- request.setEntity(new StringEntity(json));
+ HttpResponse response = null;
+ boolean responseConsumed = false;
+ try {
+ while (authenticating); // wait for authentication request (if
any) to complete (and to pick the new session key)
+ 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");
+ if (obj != null) {
+ if (obj instanceof String) {
+ request.setEntity(new StringEntity((String) obj));
+ } else {
+ JsonMapper mapper = new JsonMapper();
+
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ String json = mapper.writer().writeValueAsString(obj);
+ request.setEntity(new StringEntity(json));
+ }
+ }
+ LOG.debug("Sending POST request: " + request.toString());
+ response = httpClient.execute(request);
+ String responseStatus = (!isNullResponse(response)) ?
(response.getStatusLine().getStatusCode() + " " +
response.getStatusLine().getReasonPhrase()) : "nil";
+ LOG.debug("Received response: " + responseStatus + ", for the sent
POST request: " + request.toString());
+ if (checkAuthFailure(response, renewAndRetryOnAuthFailure)) {
+ EntityUtils.consumeQuietly(response.getEntity());
+ responseConsumed = true;
+
+ authenticate();
+ return post(path, obj, type, false);
+ }
+ return processResponse(response, type);
+ } catch (final IOException e) {
+ LOG.error("Failed in POST method due to: " + e.getMessage() +
getConnectionManagerStats(), e);
+ checkResponseTimeOut(e);
+ } finally {
+ if (!responseConsumed && response != null) {
+ EntityUtils.consumeQuietly(response.getEntity());
}
}
- final HttpResponse response = httpClient.execute(request);
+ return null;
+ }
+
+ private <T> T processResponse(HttpResponse response, final Class<T> type)
throws IOException {
+ if (isNullResponse(response)) {
+ return null;
+ }
+
+ checkResponseOK(response);
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);
- return response;
+ if (type == Boolean.class) {
+ return (T) Boolean.TRUE;
+ } else if (type == String.class) {
+ return (T) EntityUtils.toString(response.getEntity());
+ } else if (type == JsonNode.class) {
+ return (T) new
ObjectMapper().readTree(response.getEntity().getContent());
+ } else {
+ ObjectMapper mapper = new
ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
+ return mapper.readValue(response.getEntity().getContent(), type);
+ }
}
//////////////////////////////////////////////////
@@ -254,50 +383,25 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
Preconditions.checkArgument(!Strings.isNullOrEmpty(storagePoolId),
"Storage pool id cannot be null");
Preconditions.checkArgument(sizeInGb != null && sizeInGb > 0,
"Size(GB) must be greater than 0");
- HttpResponse response = null;
- try {
- Volume newVolume = new Volume();
- newVolume.setName(name);
- newVolume.setStoragePoolId(storagePoolId);
- newVolume.setVolumeSizeInGb(sizeInGb);
- if (Storage.ProvisioningType.FAT.equals(volumeType)) {
- newVolume.setVolumeType(Volume.VolumeType.ThickProvisioned);
- } else {
- newVolume.setVolumeType(Volume.VolumeType.ThinProvisioned);
- }
- // The basic allocation granularity is 8GB. The volume size will
be rounded up.
- response = post("/types/Volume/instances", newVolume);
- checkResponseOK(response);
- ObjectMapper mapper = new
ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
- Volume newVolumeObject =
mapper.readValue(response.getEntity().getContent(), Volume.class);
- return getVolume(newVolumeObject.getId());
- } catch (final IOException e) {
- LOG.error("Failed to create PowerFlex volume due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
+ Volume newVolume = new Volume();
+ newVolume.setName(name);
+ newVolume.setStoragePoolId(storagePoolId);
+ newVolume.setVolumeSizeInGb(sizeInGb);
+ if (Storage.ProvisioningType.FAT.equals(volumeType)) {
+ newVolume.setVolumeType(Volume.VolumeType.ThickProvisioned);
+ } else {
+ newVolume.setVolumeType(Volume.VolumeType.ThinProvisioned);
}
- return null;
+ // The basic allocation granularity is 8GB. The volume size will be
rounded up.
+ Volume newVolumeObject = post("/types/Volume/instances", newVolume,
Volume.class);
+ return getVolume(newVolumeObject.getId());
}
@Override
public List<Volume> listVolumes() {
- HttpResponse response = null;
- try {
- response = get("/types/Volume/instances");
- checkResponseOK(response);
- ObjectMapper mapper = new
ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
- Volume[] volumes =
mapper.readValue(response.getEntity().getContent(), Volume[].class);
+ Volume[] volumes = get("/types/Volume/instances", Volume[].class);
+ if (volumes != null) {
return Arrays.asList(volumes);
- } catch (final IOException e) {
- LOG.error("Failed to list PowerFlex volumes due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
}
return new ArrayList<>();
}
@@ -313,52 +417,24 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
}
}
}
-
return snapshotVolumes;
}
@Override
public Volume getVolume(String volumeId) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(volumeId), "Volume
id cannot be null");
-
- HttpResponse response = null;
- try {
- response = get("/instances/Volume::" + volumeId);
- checkResponseOK(response);
- ObjectMapper mapper = new
ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
- return mapper.readValue(response.getEntity().getContent(),
Volume.class);
- } catch (final IOException e) {
- LOG.error("Failed to get volume due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
- }
- return null;
+ return get("/instances/Volume::" + volumeId, Volume.class);
}
@Override
public Volume getVolumeByName(String name) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(name), "Volume name
cannot be null");
- HttpResponse response = null;
- try {
- Volume searchVolume = new Volume();
- searchVolume.setName(name);
- response = post("/types/Volume/instances/action/queryIdByKey",
searchVolume);
- checkResponseOK(response);
- String volumeId = EntityUtils.toString(response.getEntity());
- if (!Strings.isNullOrEmpty(volumeId)) {
- return getVolume(volumeId.replace("\"", ""));
- }
- } catch (final IOException e) {
- LOG.error("Failed to get volume due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
+ Volume searchVolume = new Volume();
+ searchVolume.setName(name);
+ String volumeId = post("/types/Volume/instances/action/queryIdByKey",
searchVolume, String.class);
+ if (!Strings.isNullOrEmpty(volumeId)) {
+ return getVolume(volumeId.replace("\"", ""));
}
return null;
}
@@ -368,20 +444,11 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
Preconditions.checkArgument(!Strings.isNullOrEmpty(volumeId), "Volume
id cannot be null");
Preconditions.checkArgument(!Strings.isNullOrEmpty(newName), "New name
for volume cannot be null");
- HttpResponse response = null;
- try {
- response = post(
- "/instances/Volume::" + volumeId + "/action/setVolumeName",
- String.format("{\"newName\":\"%s\"}", newName));
- checkResponseOK(response);
- return true;
- } catch (final IOException e) {
- LOG.error("Failed to rename PowerFlex volume due to: ", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
+ Boolean renameVolumeStatus = post(
+ "/instances/Volume::" + volumeId + "/action/setVolumeName",
+ String.format("{\"newName\":\"%s\"}", newName), Boolean.class);
+ if (renameVolumeStatus != null) {
+ return renameVolumeStatus;
}
return false;
}
@@ -392,21 +459,12 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
Preconditions.checkArgument(sizeInGB != null && (sizeInGB > 0 &&
sizeInGB % 8 == 0),
"Size(GB) must be greater than 0 and in granularity of 8");
- HttpResponse response = null;
- try {
- // Volume capacity can only be increased. sizeInGB must be a
positive number in granularity of 8 GB.
- response = post(
- "/instances/Volume::" + volumeId + "/action/setVolumeSize",
- String.format("{\"sizeInGB\":\"%s\"}",
sizeInGB.toString()));
- checkResponseOK(response);
+ // Volume capacity can only be increased. sizeInGB must be a positive
number in granularity of 8 GB.
+ Boolean resizeVolumeStatus = post(
+ "/instances/Volume::" + volumeId + "/action/setVolumeSize",
+ String.format("{\"sizeInGB\":\"%s\"}", sizeInGB.toString()),
Boolean.class);
+ if (resizeVolumeStatus != null && resizeVolumeStatus.booleanValue()) {
return getVolume(volumeId);
- } catch (final IOException e) {
- LOG.error("Failed to resize PowerFlex volume due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
}
return null;
}
@@ -426,33 +484,19 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
public SnapshotGroup takeSnapshot(final Map<String, String>
srcVolumeDestSnapshotMap) {
Preconditions.checkArgument(srcVolumeDestSnapshotMap != null &&
!srcVolumeDestSnapshotMap.isEmpty(), "srcVolumeDestSnapshotMap cannot be null");
- HttpResponse response = null;
- try {
- final List<SnapshotDef> defs = new ArrayList<>();
- for (final String volumeId : srcVolumeDestSnapshotMap.keySet()) {
- final SnapshotDef snapshotDef = new SnapshotDef();
- snapshotDef.setVolumeId(volumeId);
- String snapshotName = srcVolumeDestSnapshotMap.get(volumeId);
- if (!Strings.isNullOrEmpty(snapshotName)) {
-
snapshotDef.setSnapshotName(srcVolumeDestSnapshotMap.get(volumeId));
- }
- defs.add(snapshotDef);
- }
- final SnapshotDefs snapshotDefs = new SnapshotDefs();
- snapshotDefs.setSnapshotDefs(defs.toArray(new SnapshotDef[0]));
- response = post("/instances/System/action/snapshotVolumes",
snapshotDefs);
- checkResponseOK(response);
- ObjectMapper mapper = new
ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
- return mapper.readValue(response.getEntity().getContent(),
SnapshotGroup.class);
- } catch (final IOException e) {
- LOG.error("Failed to take snapshot due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
+ final List<SnapshotDef> defs = new ArrayList<>();
+ for (final String volumeId : srcVolumeDestSnapshotMap.keySet()) {
+ final SnapshotDef snapshotDef = new SnapshotDef();
+ snapshotDef.setVolumeId(volumeId);
+ String snapshotName = srcVolumeDestSnapshotMap.get(volumeId);
+ if (!Strings.isNullOrEmpty(snapshotName)) {
+
snapshotDef.setSnapshotName(srcVolumeDestSnapshotMap.get(volumeId));
}
+ defs.add(snapshotDef);
}
- return null;
+ final SnapshotDefs snapshotDefs = new SnapshotDefs();
+ snapshotDefs.setSnapshotDefs(defs.toArray(new SnapshotDef[0]));
+ return post("/instances/System/action/snapshotVolumes", snapshotDefs,
SnapshotGroup.class);
}
@Override
@@ -514,22 +558,12 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
Preconditions.checkArgument(!Strings.isNullOrEmpty(systemId), "System
id cannot be null");
Preconditions.checkArgument(!Strings.isNullOrEmpty(snapshotGroupId),
"Snapshot group id cannot be null");
- HttpResponse response = null;
- try {
- response = post(
- "/instances/System::" + systemId +
"/action/removeConsistencyGroupSnapshots",
- String.format("{\"snapGroupId\":\"%s\"}",
snapshotGroupId));
- checkResponseOK(response);
- JsonNode node = new
ObjectMapper().readTree(response.getEntity().getContent());
+ JsonNode node = post(
+ "/instances/System::" + systemId +
"/action/removeConsistencyGroupSnapshots",
+ String.format("{\"snapGroupId\":\"%s\"}", snapshotGroupId),
JsonNode.class);
+ if (node != null) {
JsonNode noOfVolumesNode = node.get("numberOfVolumes");
return noOfVolumesNode.asInt();
- } catch (final IOException e) {
- LOG.error("Failed to delete PowerFlex snapshot group due to: " +
e.getMessage(), e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
}
return -1;
}
@@ -539,31 +573,18 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
Preconditions.checkArgument(!Strings.isNullOrEmpty(volumeId), "Volume
id cannot be null");
Preconditions.checkArgument(!Strings.isNullOrEmpty(snapshotVolumeName),
"Snapshot name cannot be null");
- HttpResponse response = null;
- try {
- final SnapshotDef[] snapshotDef = new SnapshotDef[1];
- snapshotDef[0] = new SnapshotDef();
- snapshotDef[0].setVolumeId(volumeId);
- snapshotDef[0].setSnapshotName(snapshotVolumeName);
- final SnapshotDefs snapshotDefs = new SnapshotDefs();
- snapshotDefs.setSnapshotDefs(snapshotDef);
-
- response = post("/instances/System/action/snapshotVolumes",
snapshotDefs);
- checkResponseOK(response);
- ObjectMapper mapper = new
ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
- SnapshotGroup snapshotGroup =
mapper.readValue(response.getEntity().getContent(), SnapshotGroup.class);
- if (snapshotGroup != null) {
- List<String> volumeIds = snapshotGroup.getVolumeIds();
- if (volumeIds != null && !volumeIds.isEmpty()) {
- return getVolume(volumeIds.get(0));
- }
- }
- } catch (final IOException e) {
- LOG.error("Failed to take snapshot due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
+ final SnapshotDef[] snapshotDef = new SnapshotDef[1];
+ snapshotDef[0] = new SnapshotDef();
+ snapshotDef[0].setVolumeId(volumeId);
+ snapshotDef[0].setSnapshotName(snapshotVolumeName);
+ final SnapshotDefs snapshotDefs = new SnapshotDefs();
+ snapshotDefs.setSnapshotDefs(snapshotDef);
+
+ SnapshotGroup snapshotGroup =
post("/instances/System/action/snapshotVolumes", snapshotDefs,
SnapshotGroup.class);
+ if (snapshotGroup != null) {
+ List<String> volumeIds = snapshotGroup.getVolumeIds();
+ if (volumeIds != null && !volumeIds.isEmpty()) {
+ return getVolume(volumeIds.get(0));
}
}
return null;
@@ -574,34 +595,25 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
Preconditions.checkArgument(!Strings.isNullOrEmpty(sourceSnapshotVolumeId),
"Source snapshot volume id cannot be null");
Preconditions.checkArgument(!Strings.isNullOrEmpty(destVolumeId),
"Destination volume id cannot be null");
- HttpResponse response = null;
- try {
- Volume sourceSnapshotVolume = getVolume(sourceSnapshotVolumeId);
- if (sourceSnapshotVolume == null) {
- throw new CloudRuntimeException("Source snapshot volume: " +
sourceSnapshotVolumeId + " doesn't exists");
- }
+ Volume sourceSnapshotVolume = getVolume(sourceSnapshotVolumeId);
+ if (sourceSnapshotVolume == null) {
+ throw new CloudRuntimeException("Source snapshot volume: " +
sourceSnapshotVolumeId + " doesn't exists");
+ }
- Volume destVolume = getVolume(destVolumeId);
- if (sourceSnapshotVolume == null) {
- throw new CloudRuntimeException("Destination volume: " +
destVolumeId + " doesn't exists");
- }
+ Volume destVolume = getVolume(destVolumeId);
+ if (sourceSnapshotVolume == null) {
+ throw new CloudRuntimeException("Destination volume: " +
destVolumeId + " doesn't exists");
+ }
- if
(!sourceSnapshotVolume.getVtreeId().equals(destVolume.getVtreeId())) {
- throw new CloudRuntimeException("Unable to revert, source
snapshot volume and destination volume doesn't belong to same volume tree");
- }
+ if
(!sourceSnapshotVolume.getVtreeId().equals(destVolume.getVtreeId())) {
+ throw new CloudRuntimeException("Unable to revert, source snapshot
volume and destination volume doesn't belong to same volume tree");
+ }
- response = post(
- "/instances/Volume::" + destVolumeId +
"/action/overwriteVolumeContent",
-
String.format("{\"srcVolumeId\":\"%s\",\"allowOnExtManagedVol\":\"TRUE\"}",
sourceSnapshotVolumeId));
- checkResponseOK(response);
- return true;
- } catch (final IOException e) {
- LOG.error("Failed to map PowerFlex volume due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
+ Boolean overwriteVolumeContentStatus = post(
+ "/instances/Volume::" + destVolumeId +
"/action/overwriteVolumeContent",
+
String.format("{\"srcVolumeId\":\"%s\",\"allowOnExtManagedVol\":\"TRUE\"}",
sourceSnapshotVolumeId), Boolean.class);
+ if (overwriteVolumeContentStatus != null) {
+ return overwriteVolumeContentStatus;
}
return false;
}
@@ -611,24 +623,15 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
Preconditions.checkArgument(!Strings.isNullOrEmpty(volumeId), "Volume
id cannot be null");
Preconditions.checkArgument(!Strings.isNullOrEmpty(sdcId), "Sdc Id
cannot be null");
- HttpResponse response = null;
- try {
- if (isVolumeMappedToSdc(volumeId, sdcId)) {
- return true;
- }
-
- response = post(
- "/instances/Volume::" + volumeId + "/action/addMappedSdc",
-
String.format("{\"sdcId\":\"%s\",\"allowMultipleMappings\":\"TRUE\"}", sdcId));
- checkResponseOK(response);
+ if (isVolumeMappedToSdc(volumeId, sdcId)) {
return true;
- } catch (final IOException e) {
- LOG.error("Failed to map PowerFlex volume due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
+ }
+
+ Boolean mapVolumeToSdcStatus = post(
+ "/instances/Volume::" + volumeId + "/action/addMappedSdc",
+
String.format("{\"sdcId\":\"%s\",\"allowMultipleMappings\":\"TRUE\"}", sdcId),
Boolean.class);
+ if (mapVolumeToSdcStatus != null) {
+ return mapVolumeToSdcStatus;
}
return false;
}
@@ -642,31 +645,22 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
Preconditions.checkArgument(bandwidthLimitInKbps != null &&
(bandwidthLimitInKbps == 0 || (bandwidthLimitInKbps > 0 && bandwidthLimitInKbps
% 1024 == 0)),
"Bandwidth limit(Kbps) must be 0 (unlimited) or in granularity
of 1024");
- HttpResponse response = null;
- try {
- if (mapVolumeToSdc(volumeId, sdcId)) {
- long iopsLimitVal = 0;
- if (iopsLimit != null && iopsLimit.longValue() > 0) {
- iopsLimitVal = iopsLimit.longValue();
- }
-
- long bandwidthLimitInKbpsVal = 0;
- if (bandwidthLimitInKbps != null &&
bandwidthLimitInKbps.longValue() > 0) {
- bandwidthLimitInKbpsVal = bandwidthLimitInKbps.longValue();
- }
+ if (mapVolumeToSdc(volumeId, sdcId)) {
+ long iopsLimitVal = 0;
+ if (iopsLimit != null && iopsLimit.longValue() > 0) {
+ iopsLimitVal = iopsLimit.longValue();
+ }
- response = post(
- "/instances/Volume::" + volumeId +
"/action/setMappedSdcLimits",
-
String.format("{\"sdcId\":\"%s\",\"bandwidthLimitInKbps\":\"%d\",\"iopsLimit\":\"%d\"}",
sdcId, bandwidthLimitInKbpsVal, iopsLimitVal));
- checkResponseOK(response);
- return true;
+ long bandwidthLimitInKbpsVal = 0;
+ if (bandwidthLimitInKbps != null &&
bandwidthLimitInKbps.longValue() > 0) {
+ bandwidthLimitInKbpsVal = bandwidthLimitInKbps.longValue();
}
- } catch (final IOException e) {
- LOG.error("Failed to map PowerFlex volume with limits due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
+
+ Boolean setVolumeSdcLimitsStatus = post(
+ "/instances/Volume::" + volumeId +
"/action/setMappedSdcLimits",
+
String.format("{\"sdcId\":\"%s\",\"bandwidthLimitInKbps\":\"%d\",\"iopsLimit\":\"%d\"}",
sdcId, bandwidthLimitInKbpsVal, iopsLimitVal), Boolean.class);
+ if (setVolumeSdcLimitsStatus != null) {
+ return setVolumeSdcLimitsStatus;
}
}
return false;
@@ -677,21 +671,12 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
Preconditions.checkArgument(!Strings.isNullOrEmpty(volumeId), "Volume
id cannot be null");
Preconditions.checkArgument(!Strings.isNullOrEmpty(sdcId), "Sdc Id
cannot be null");
- HttpResponse response = null;
- try {
- if (isVolumeMappedToSdc(volumeId, sdcId)) {
- response = post(
- "/instances/Volume::" + volumeId +
"/action/removeMappedSdc",
-
String.format("{\"sdcId\":\"%s\",\"skipApplianceValidation\":\"TRUE\"}",
sdcId));
- checkResponseOK(response);
- return true;
- }
- } catch (final IOException e) {
- LOG.error("Failed to unmap PowerFlex volume due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
+ if (isVolumeMappedToSdc(volumeId, sdcId)) {
+ Boolean unmapVolumeFromSdcStatus = post(
+ "/instances/Volume::" + volumeId +
"/action/removeMappedSdc",
+
String.format("{\"sdcId\":\"%s\",\"skipApplianceValidation\":\"TRUE\"}",
sdcId), Boolean.class);
+ if (unmapVolumeFromSdcStatus != null) {
+ return unmapVolumeFromSdcStatus;
}
}
return false;
@@ -701,30 +686,21 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
public boolean unmapVolumeFromAllSdcs(final String volumeId) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(volumeId), "Volume
id cannot be null");
- HttpResponse response = null;
- try {
- Volume volume = getVolume(volumeId);
- if (volume == null) {
- return false;
- }
-
- List<SdcMappingInfo> mappedSdcList = volume.getMappedSdcList();
- if (mappedSdcList == null || mappedSdcList.isEmpty()) {
- return true;
- }
+ Volume volume = getVolume(volumeId);
+ if (volume == null) {
+ return false;
+ }
- response = post(
- "/instances/Volume::" + volumeId +
"/action/removeMappedSdc",
- "{\"allSdcs\": \"\"}");
- checkResponseOK(response);
+ List<SdcMappingInfo> mappedSdcList = volume.getMappedSdcList();
+ if (mappedSdcList == null || mappedSdcList.isEmpty()) {
return true;
- } catch (final IOException e) {
- LOG.error("Failed to unmap PowerFlex volume from all SDCs due
to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
+ }
+
+ Boolean unmapVolumeFromAllSdcsStatus = post(
+ "/instances/Volume::" + volumeId + "/action/removeMappedSdc",
+ "{\"allSdcs\": \"\"}", Boolean.class);
+ if (unmapVolumeFromAllSdcsStatus != null) {
+ return unmapVolumeFromAllSdcsStatus;
}
return false;
}
@@ -759,23 +735,14 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
public boolean deleteVolume(final String volumeId) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(volumeId), "Volume
id cannot be null");
- HttpResponse response = null;
try {
- try {
- unmapVolumeFromAllSdcs(volumeId);
- } catch (Exception ignored) {}
- response = post(
- "/instances/Volume::" + volumeId + "/action/removeVolume",
- "{\"removeMode\":\"ONLY_ME\"}");
- checkResponseOK(response);
- return true;
- } catch (final IOException e) {
- LOG.error("Failed to delete PowerFlex volume due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
+ unmapVolumeFromAllSdcs(volumeId);
+ } catch (Exception ignored) {}
+ Boolean removeVolumeStatus = post(
+ "/instances/Volume::" + volumeId + "/action/removeVolume",
+ "{\"removeMode\":\"ONLY_ME\"}", Boolean.class);
+ if (removeVolumeStatus != null) {
+ return removeVolumeStatus;
}
return false;
}
@@ -797,21 +764,8 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
LOG.debug("Migrating the volume: " + srcVolumeId + " on the src
pool: " + srcPoolId + " to the dest pool: " + destPoolId +
" in the same PowerFlex cluster");
- HttpResponse response = null;
- try {
- response = post(
- "/instances/Volume::" + srcVolumeId +
"/action/migrateVTree",
- String.format("{\"destSPId\":\"%s\"}", destPoolId));
- checkResponseOK(response);
- } catch (final IOException e) {
- LOG.error("Unable to migrate PowerFlex volume due to: ", e);
- checkResponseTimeOut(e);
- throw e;
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
- }
+ post("/instances/Volume::" + srcVolumeId + "/action/migrateVTree",
+ String.format("{\"destSPId\":\"%s\"}", destPoolId),
Boolean.class);
LOG.debug("Wait until the migration is complete for the volume: "
+ srcVolumeId);
long migrationStartTime = System.currentTimeMillis();
@@ -899,22 +853,9 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
return null;
}
- HttpResponse response = null;
- try {
- response = get("/instances/VTree::" + volumeTreeId);
- checkResponseOK(response);
- ObjectMapper mapper = new
ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
- VTree volumeTree =
mapper.readValue(response.getEntity().getContent(), VTree.class);
- if (volumeTree != null && volumeTree.getVTreeMigrationInfo() !=
null) {
- return volumeTree.getVTreeMigrationInfo().getMigrationStatus();
- }
- } catch (final IOException e) {
- LOG.error("Failed to migrate PowerFlex volume due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
+ VTree volumeTree = get("/instances/VTree::" + volumeTreeId,
VTree.class);
+ if (volumeTree != null && volumeTree.getVTreeMigrationInfo() != null) {
+ return volumeTree.getVTreeMigrationInfo().getMigrationStatus();
}
return null;
}
@@ -922,53 +863,49 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
private boolean rollbackVolumeMigration(final String srcVolumeId) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(srcVolumeId), "src
volume id cannot be null");
- HttpResponse response = null;
- try {
- Volume volume = getVolume(srcVolumeId);
- VTreeMigrationInfo.MigrationStatus migrationStatus =
getVolumeTreeMigrationStatus(volume.getVtreeId());
- if (migrationStatus != null && migrationStatus ==
VTreeMigrationInfo.MigrationStatus.NotInMigration) {
- LOG.debug("Volume: " + srcVolumeId + " is not migrating, no
need to rollback");
- return true;
- }
+ Volume volume = getVolume(srcVolumeId);
+ if (volume == null) {
+ LOG.warn("Unable to rollback volume migration, couldn't get
details for the volume: " + srcVolumeId);
+ return false;
+ }
- pauseVolumeMigration(srcVolumeId, true); // Pause forcefully
- // Wait few secs for volume migration to change to Paused state
- boolean paused = false;
- int retryCount = 3;
- while (retryCount > 0) {
- try {
- Thread.sleep(3000); // Try after few secs
- migrationStatus =
getVolumeTreeMigrationStatus(volume.getVtreeId()); // Get updated migration
status
- if (migrationStatus != null && migrationStatus ==
VTreeMigrationInfo.MigrationStatus.Paused) {
- LOG.debug("Migration for the volume: " + srcVolumeId +
" paused");
- paused = true;
- break;
- }
- } catch (Exception ex) {
- LOG.warn("Exception while checking for migration pause
status of the volume: " + srcVolumeId + " - " + ex.getLocalizedMessage());
- // don't do anything
- } finally {
- retryCount--;
+ VTreeMigrationInfo.MigrationStatus migrationStatus =
getVolumeTreeMigrationStatus(volume.getVtreeId());
+ if (migrationStatus != null && migrationStatus ==
VTreeMigrationInfo.MigrationStatus.NotInMigration) {
+ LOG.debug("Volume: " + srcVolumeId + " is not migrating, no need
to rollback");
+ return true;
+ }
+
+ pauseVolumeMigration(srcVolumeId, true); // Pause forcefully
+ // Wait few secs for volume migration to change to Paused state
+ boolean paused = false;
+ int retryCount = 3;
+ while (retryCount > 0) {
+ try {
+ Thread.sleep(3000); // Try after few secs
+ migrationStatus =
getVolumeTreeMigrationStatus(volume.getVtreeId()); // Get updated migration
status
+ if (migrationStatus != null && migrationStatus ==
VTreeMigrationInfo.MigrationStatus.Paused) {
+ LOG.debug("Migration for the volume: " + srcVolumeId + "
paused");
+ paused = true;
+ break;
}
+ } catch (Exception ex) {
+ LOG.warn("Exception while checking for migration pause status
of the volume: " + srcVolumeId + " - " + ex.getLocalizedMessage());
+ // don't do anything
+ } finally {
+ retryCount--;
}
+ }
- if (paused) {
- // Rollback migration to the src pool (should be quick)
- response = post(
- "/instances/Volume::" + srcVolumeId +
"/action/migrateVTree",
- String.format("{\"destSPId\":\"%s\"}",
volume.getStoragePoolId()));
- checkResponseOK(response);
- return true;
- } else {
- LOG.warn("Migration for the volume: " + srcVolumeId + " didn't
pause, couldn't rollback");
- }
- } catch (final IOException e) {
- LOG.error("Failed to rollback volume migration due to: ", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
+ if (paused) {
+ // Rollback migration to the src pool (should be quick)
+ Boolean migrateVTreeStatus = post(
+ "/instances/Volume::" + srcVolumeId +
"/action/migrateVTree",
+ String.format("{\"destSPId\":\"%s\"}",
volume.getStoragePoolId()), Boolean.class);
+ if (migrateVTreeStatus != null) {
+ return migrateVTreeStatus;
}
+ } else {
+ LOG.warn("Migration for the volume: " + srcVolumeId + " didn't
pause, couldn't rollback");
}
return false;
}
@@ -979,23 +916,14 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
return false;
}
- HttpResponse response = null;
- try {
- // When paused gracefully, all data currently being moved is
allowed to complete the migration.
- // When paused forcefully, migration of unfinished data is aborted
and data is left at the source, if possible.
- // Pausing forcefully carries a potential risk to data.
- response = post(
- "/instances/Volume::" + volumeId +
"/action/pauseVTreeMigration",
- String.format("{\"pauseType\":\"%s\"}", forced ?
"Forcefully" : "Gracefully"));
- checkResponseOK(response);
- return true;
- } catch (final IOException e) {
- LOG.error("Failed to pause migration of the volume due to: ", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
+ // When paused gracefully, all data currently being moved is allowed
to complete the migration.
+ // When paused forcefully, migration of unfinished data is aborted and
data is left at the source, if possible.
+ // Pausing forcefully carries a potential risk to data.
+ Boolean pauseVTreeMigrationStatus = post(
+ "/instances/Volume::" + volumeId +
"/action/pauseVTreeMigration",
+ String.format("{\"pauseType\":\"%s\"}", forced ? "Forcefully"
: "Gracefully"), Boolean.class);
+ if (pauseVTreeMigrationStatus != null) {
+ return pauseVTreeMigrationStatus;
}
return false;
}
@@ -1006,20 +934,9 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
@Override
public List<StoragePool> listStoragePools() {
- HttpResponse response = null;
- try {
- response = get("/types/StoragePool/instances");
- checkResponseOK(response);
- ObjectMapper mapper = new
ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
- StoragePool[] pools =
mapper.readValue(response.getEntity().getContent(), StoragePool[].class);
+ StoragePool[] pools = get("/types/StoragePool/instances",
StoragePool[].class);
+ if (pools != null) {
return Arrays.asList(pools);
- } catch (final IOException e) {
- LOG.error("Failed to list PowerFlex storage pools due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
}
return new ArrayList<>();
}
@@ -1027,72 +944,29 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
@Override
public StoragePool getStoragePool(String poolId) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(poolId), "Storage
pool id cannot be null");
-
- HttpResponse response = null;
- try {
- response = get("/instances/StoragePool::" + poolId);
- checkResponseOK(response);
- ObjectMapper mapper = new
ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
- return mapper.readValue(response.getEntity().getContent(),
StoragePool.class);
- } catch (final IOException e) {
- LOG.error("Failed to get storage pool due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
- }
- return null;
+ return get("/instances/StoragePool::" + poolId, StoragePool.class);
}
@Override
public StoragePoolStatistics getStoragePoolStatistics(String poolId) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(poolId), "Storage
pool id cannot be null");
-
- HttpResponse response = null;
- try {
- response = get("/instances/StoragePool::" + poolId +
"/relationships/Statistics");
- checkResponseOK(response);
- ObjectMapper mapper = new
ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
- return mapper.readValue(response.getEntity().getContent(),
StoragePoolStatistics.class);
- } catch (final IOException e) {
- LOG.error("Failed to get storage pool due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
- }
- return null;
+ return get("/instances/StoragePool::" + poolId +
"/relationships/Statistics", StoragePoolStatistics.class);
}
@Override
public VolumeStatistics getVolumeStatistics(String volumeId) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(volumeId), "Volume
id cannot be null");
- HttpResponse response = null;
- try {
- Volume volume = getVolume(volumeId);
- if (volume != null) {
- String volumeTreeId = volume.getVtreeId();
- if (!Strings.isNullOrEmpty(volumeTreeId)) {
- response = get("/instances/VTree::" + volumeTreeId +
"/relationships/Statistics");
- checkResponseOK(response);
- ObjectMapper mapper = new
ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
- VolumeStatistics volumeStatistics =
mapper.readValue(response.getEntity().getContent(), VolumeStatistics.class);
- if (volumeStatistics != null) {
-
volumeStatistics.setAllocatedSizeInKb(volume.getSizeInKb());
- return volumeStatistics;
- }
+ Volume volume = getVolume(volumeId);
+ if (volume != null) {
+ String volumeTreeId = volume.getVtreeId();
+ if (!Strings.isNullOrEmpty(volumeTreeId)) {
+ VolumeStatistics volumeStatistics = get("/instances/VTree::" +
volumeTreeId + "/relationships/Statistics", VolumeStatistics.class);
+ if (volumeStatistics != null) {
+
volumeStatistics.setAllocatedSizeInKb(volume.getSizeInKb());
+ return volumeStatistics;
}
}
- } catch (final IOException e) {
- LOG.error("Failed to get volume stats due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
}
return null;
@@ -1102,22 +976,9 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
public String getSystemId(String protectionDomainId) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(protectionDomainId),
"Protection domain id cannot be null");
- HttpResponse response = null;
- try {
- response = get("/instances/ProtectionDomain::" +
protectionDomainId);
- checkResponseOK(response);
- ObjectMapper mapper = new
ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
- ProtectionDomain protectionDomain =
mapper.readValue(response.getEntity().getContent(), ProtectionDomain.class);
- if (protectionDomain != null) {
- return protectionDomain.getSystemId();
- }
- } catch (final IOException e) {
- LOG.error("Failed to get protection domain details due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
+ ProtectionDomain protectionDomain =
get("/instances/ProtectionDomain::" + protectionDomainId,
ProtectionDomain.class);
+ if (protectionDomain != null) {
+ return protectionDomain.getSystemId();
}
return null;
}
@@ -1126,20 +987,9 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
public List<Volume> listVolumesInStoragePool(String poolId) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(poolId), "Storage
pool id cannot be null");
- HttpResponse response = null;
- try {
- response = get("/instances/StoragePool::" + poolId +
"/relationships/Volume");
- checkResponseOK(response);
- ObjectMapper mapper = new
ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
- Volume[] volumes =
mapper.readValue(response.getEntity().getContent(), Volume[].class);
+ Volume[] volumes = get("/instances/StoragePool::" + poolId +
"/relationships/Volume", Volume[].class);
+ if (volumes != null) {
return Arrays.asList(volumes);
- } catch (final IOException e) {
- LOG.error("Failed to list volumes in storage pool due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
}
return new ArrayList<>();
}
@@ -1150,20 +1000,9 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
@Override
public List<Sdc> listSdcs() {
- HttpResponse response = null;
- try {
- response = get("/types/Sdc/instances");
- checkResponseOK(response);
- ObjectMapper mapper = new
ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
- Sdc[] sdcs = mapper.readValue(response.getEntity().getContent(),
Sdc[].class);
+ Sdc[] sdcs = get("/types/Sdc/instances", Sdc[].class);
+ if (sdcs != null) {
return Arrays.asList(sdcs);
- } catch (final IOException e) {
- LOG.error("Failed to list SDCs due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
}
return new ArrayList<>();
}
@@ -1171,43 +1010,16 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
@Override
public Sdc getSdc(String sdcId) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(sdcId), "Sdc id
cannot be null");
-
- HttpResponse response = null;
- try {
- response = get("/instances/Sdc::" + sdcId);
- checkResponseOK(response);
- ObjectMapper mapper = new
ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
- return mapper.readValue(response.getEntity().getContent(),
Sdc.class);
- } catch (final IOException e) {
- LOG.error("Failed to get volume due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
- }
- return null;
+ return get("/instances/Sdc::" + sdcId, Sdc.class);
}
@Override
public Sdc getSdcByIp(String ipAddress) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(ipAddress), "IP
address cannot be null");
- HttpResponse response = null;
- try {
- response = post("/types/Sdc/instances/action/queryIdByKey",
String.format("{\"ip\":\"%s\"}", ipAddress));
- checkResponseOK(response);
- String sdcId = EntityUtils.toString(response.getEntity());
- if (!Strings.isNullOrEmpty(sdcId)) {
- return getSdc(sdcId.replace("\"", ""));
- }
- } catch (final IOException e) {
- LOG.error("Failed to get volume due to:", e);
- checkResponseTimeOut(e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
+ String sdcId = post("/types/Sdc/instances/action/queryIdByKey",
String.format("{\"ip\":\"%s\"}", ipAddress), String.class);
+ if (!Strings.isNullOrEmpty(sdcId)) {
+ return getSdc(sdcId.replace("\"", ""));
}
return null;
}
@@ -1252,4 +1064,27 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
return false;
}
+
+ private String getConnectionManagerStats() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("\n").append("Client Connection Manager Stats => ");
+ if (connectionManager != null) {
+ sb.append("MaxTotal:
").append(connectionManager.getMaxTotal()).append(", ");
+ sb.append("DefaultMaxPerRoute:
").append(connectionManager.getDefaultMaxPerRoute());
+
+ PoolStats poolStats = connectionManager.getTotalStats();
+ if (poolStats != null) {
+ sb.append(", ");
+ sb.append("Available:
").append(poolStats.getAvailable()).append(", ");
+ sb.append("Leased: ").append(poolStats.getLeased()).append(",
");
+ sb.append("Max: ").append(poolStats.getMax()).append(", ");
+ sb.append("Pending: ").append(poolStats.getPending());
+ }
+ } else {
+ sb.append("NULL");
+ }
+
+ sb.append("\n");
+ return sb.toString();
+ }
}
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 5c9ddea..edebdac 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
@@ -106,7 +106,8 @@ public class ScaleIOPrimaryDataStoreLifeCycle implements
PrimaryDataStoreLifeCyc
private org.apache.cloudstack.storage.datastore.api.StoragePool
findStoragePool(String url, String username, String password, String
storagePoolName) {
try {
final int clientTimeout =
StorageManager.STORAGE_POOL_CLIENT_TIMEOUT.value();
- ScaleIOGatewayClient client = ScaleIOGatewayClient.getClient(url,
username, password, false, clientTimeout);
+ final int clientMaxConnections =
StorageManager.STORAGE_POOL_CLIENT_MAX_CONNECTIONS.value();
+ ScaleIOGatewayClient client = ScaleIOGatewayClient.getClient(url,
username, password, false, clientTimeout, clientMaxConnections);
List<org.apache.cloudstack.storage.datastore.api.StoragePool>
storagePools = client.listStoragePools();
for (org.apache.cloudstack.storage.datastore.api.StoragePool pool
: storagePools) {
if (pool.getName().equals(storagePoolName)) {
@@ -121,9 +122,9 @@ public class ScaleIOPrimaryDataStoreLifeCycle implements
PrimaryDataStoreLifeCyc
}
} catch (NoSuchAlgorithmException | KeyManagementException |
URISyntaxException e) {
LOGGER.error("Failed to add storage pool", e);
- throw new CloudRuntimeException("Failed to establish connection
with PowerFlex Gateway to validate storage pool");
+ throw new CloudRuntimeException("Failed to establish connection
with PowerFlex Gateway to find and validate storage pool: " + storagePoolName);
}
- throw new CloudRuntimeException("Failed to find the provided storage
pool name in discovered PowerFlex storage pools");
+ throw new CloudRuntimeException("Failed to find the provided storage
pool name: " + storagePoolName + " in the discovered PowerFlex storage pools");
}
@SuppressWarnings("unchecked")
diff --git
a/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImplTest.java
b/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImplTest.java
index 1082310..80a78c8 100644
---
a/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImplTest.java
+++
b/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImplTest.java
@@ -19,30 +19,179 @@
package org.apache.cloudstack.storage.datastore.client;
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.containing;
+import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.ok;
+import static com.github.tomakehurst.wiremock.client.WireMock.post;
+import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.unauthorized;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+import static
com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
+
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.storage.datastore.api.Volume;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
+import com.cloud.storage.Storage;
import com.cloud.utils.exception.CloudRuntimeException;
+import com.github.tomakehurst.wiremock.client.BasicCredentials;
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
@RunWith(MockitoJUnitRunner.class)
public class ScaleIOGatewayClientImplTest {
+ private final int port = 443;
+ private final int timeout = 30;
+ private final int maxConnections = 50;
+ private final String username = "admin";
+ private final String password = "P@ssword123";
+ private final String sessionKey =
"YWRtaW46MTYyMzM0OTc4NDk0MTo2MWQ2NGQzZWJhMTVmYTVkNDIwNjZmOWMwZDg0ZGZmOQ";
+ private ScaleIOGatewayClient client = null;
- ScaleIOGatewayClientImpl client;
+ @Rule
+ public WireMockRule wireMockRule = new WireMockRule(wireMockConfig()
+ .httpsPort(port)
+ .needClientAuth(false)
+ .basicAdminAuthenticator(username, password)
+ .bindAddress("localhost"));
@Before
public void setUp() throws Exception {
+ wireMockRule.stubFor(get("/api/login")
+ .willReturn(ok()
+ .withHeader("Content-Type",
"application/json;charset=UTF-8")
+ .withBody(sessionKey)));
+
+ client = new ScaleIOGatewayClientImpl("https://localhost/api",
username, password, false, timeout, maxConnections);
+
+ wireMockRule.stubFor(post("/api/types/Volume/instances")
+ .willReturn(aResponse()
+ .withHeader("Content-Type",
"application/json;charset=UTF-8")
+ .withStatus(200)
+ .withBody("{\"id\":\"c948d0b10000000a\"}")));
+
+ wireMockRule.stubFor(get("/api/instances/Volume::c948d0b10000000a")
+ .willReturn(aResponse()
+ .withHeader("Content-Type",
"application/json;charset=UTF-8")
+ .withStatus(200)
+
.withBody("{\"storagePoolId\":\"4daaa55e00000000\",\"dataLayout\":\"MediumGranularity\",\"vtreeId\":\"657e289500000009\","
+ +
"\"sizeInKb\":8388608,\"snplIdOfAutoSnapshot\":null,\"volumeType\":\"ThinProvisioned\",\"consistencyGroupId\":null,"
+ +
"\"ancestorVolumeId\":null,\"notGenuineSnapshot\":false,\"accessModeLimit\":\"ReadWrite\",\"secureSnapshotExpTime\":0,"
+ +
"\"useRmcache\":false,\"managedBy\":\"ScaleIO\",\"lockedAutoSnapshot\":false,\"lockedAutoSnapshotMarkedForRemoval\":false,"
+ +
"\"autoSnapshotGroupId\":null,\"compressionMethod\":\"Invalid\",\"pairIds\":null,\"timeStampIsAccurate\":false,\"mappedSdcInfo\":null,"
+ +
"\"retentionLevels\":[],\"snplIdOfSourceVolume\":null,\"volumeReplicationState\":\"UnmarkedForReplication\",\"replicationJournalVolume\":false,"
+ +
"\"replicationTimeStamp\":0,\"originalExpiryTime\":0,\"creationTime\":1623335880,\"name\":\"testvolume\",\"id\":\"c948d0b10000000a\"}")));
}
@After
public void tearDown() throws Exception {
}
+ @Test
+ public void testClientAuthSuccess() {
+ Assert.assertNotNull(client);
+ wireMockRule.verify(getRequestedFor(urlEqualTo("/api/login"))
+ .withBasicAuth(new BasicCredentials(username, password)));
+
+ wireMockRule.stubFor(get("/api/types/StoragePool/instances")
+ .willReturn(aResponse()
+ .withHeader("Content-Type",
"application/json;charset=UTF-8")
+ .withStatus(200)
+ .withBody("")));
+
+ client.listStoragePools();
+
+
wireMockRule.verify(getRequestedFor(urlEqualTo("/api/types/StoragePool/instances"))
+ .withBasicAuth(new BasicCredentials(username, sessionKey)));
+ }
+
@Test(expected = CloudRuntimeException.class)
- public void testClient() throws Exception {
- client = (ScaleIOGatewayClientImpl)
ScaleIOGatewayClient.getClient("https://10.2.3.149/api",
- "admin", "P@ssword123", false, 60);
+ public void testClientAuthFailure() throws Exception {
+ wireMockRule.stubFor(get("/api/login")
+ .willReturn(unauthorized()
+ .withHeader("Content-Type",
"application/json;charset=UTF-8")
+ .withBody("")));
+
+ new ScaleIOGatewayClientImpl("https://localhost/api", username,
password, false, timeout, maxConnections);
+ }
+
+ @Test(expected = ServerApiException.class)
+ public void testRequestTimeout() {
+ Assert.assertNotNull(client);
+ wireMockRule.verify(getRequestedFor(urlEqualTo("/api/login"))
+ .withBasicAuth(new BasicCredentials(username, password)));
+
+ wireMockRule.stubFor(get("/api/types/StoragePool/instances")
+ .willReturn(aResponse()
+ .withHeader("Content-Type",
"application/json;charset=UTF-8")
+ .withStatus(200)
+ .withFixedDelay(2 * timeout * 1000)
+ .withBody("")));
+
+ client.listStoragePools();
+ }
+
+ @Test
+ public void testCreateSingleVolume() {
+ Assert.assertNotNull(client);
+ wireMockRule.verify(getRequestedFor(urlEqualTo("/api/login"))
+ .withBasicAuth(new BasicCredentials(username, password)));
+
+ final String volumeName = "testvolume";
+ final String scaleIOStoragePoolId = "4daaa55e00000000";
+ final int sizeInGb = 8;
+ Volume scaleIOVolume = client.createVolume(volumeName,
scaleIOStoragePoolId, sizeInGb, Storage.ProvisioningType.THIN);
+
+
wireMockRule.verify(postRequestedFor(urlEqualTo("/api/types/Volume/instances"))
+ .withBasicAuth(new BasicCredentials(username, sessionKey))
+ .withRequestBody(containing("\"name\":\"" + volumeName + "\""))
+ .withHeader("Content-Type", equalTo("application/json")));
+
wireMockRule.verify(getRequestedFor(urlEqualTo("/api/instances/Volume::c948d0b10000000a"))
+ .withBasicAuth(new BasicCredentials(username, sessionKey)));
+
+ Assert.assertNotNull(scaleIOVolume);
+ Assert.assertEquals(scaleIOVolume.getId(), "c948d0b10000000a");
+ Assert.assertEquals(scaleIOVolume.getName(), volumeName);
+ Assert.assertEquals(scaleIOVolume.getStoragePoolId(),
scaleIOStoragePoolId);
+ Assert.assertEquals(scaleIOVolume.getSizeInKb(), Long.valueOf(sizeInGb
* 1024 * 1024));
+ Assert.assertEquals(scaleIOVolume.getVolumeType(),
Volume.VolumeType.ThinProvisioned);
+ }
+
+ @Test
+ public void testCreateMultipleVolumes() {
+ Assert.assertNotNull(client);
+ wireMockRule.verify(getRequestedFor(urlEqualTo("/api/login"))
+ .withBasicAuth(new BasicCredentials(username, password)));
+
+ final String volumeNamePrefix = "testvolume_";
+ final String scaleIOStoragePoolId = "4daaa55e00000000";
+ final int sizeInGb = 8;
+ final int volumesCount = 1000;
+
+ for (int i = 1; i <= volumesCount; i++) {
+ String volumeName = volumeNamePrefix + i;
+ Volume scaleIOVolume = client.createVolume(volumeName,
scaleIOStoragePoolId, sizeInGb, Storage.ProvisioningType.THIN);
+
+ Assert.assertNotNull(scaleIOVolume);
+ Assert.assertEquals(scaleIOVolume.getId(), "c948d0b10000000a");
+ Assert.assertEquals(scaleIOVolume.getStoragePoolId(),
scaleIOStoragePoolId);
+ Assert.assertEquals(scaleIOVolume.getSizeInKb(),
Long.valueOf(sizeInGb * 1024 * 1024));
+ Assert.assertEquals(scaleIOVolume.getVolumeType(),
Volume.VolumeType.ThinProvisioned);
+ }
+
+ wireMockRule.verify(volumesCount,
postRequestedFor(urlEqualTo("/api/types/Volume/instances"))
+ .withBasicAuth(new BasicCredentials(username, sessionKey))
+ .withRequestBody(containing("\"name\":\"" + volumeNamePrefix))
+ .withHeader("Content-Type", equalTo("application/json")));
+ wireMockRule.verify(volumesCount,
getRequestedFor(urlEqualTo("/api/instances/Volume::c948d0b10000000a"))
+ .withBasicAuth(new BasicCredentials(username, sessionKey)));
}
}
\ No newline at end of file
diff --git
a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
index cb7e4b5..f431945 100755
--- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -474,6 +474,9 @@ public class ConfigurationManagerImpl extends ManagerBase
implements Configurati
configValuesForValidation.add("externaldhcp.vmip.max.retry");
configValuesForValidation.add("externaldhcp.vmipFetch.threadPool.max");
configValuesForValidation.add("remote.access.vpn.psk.length");
+
configValuesForValidation.add(StorageManager.STORAGE_POOL_DISK_WAIT.key());
+
configValuesForValidation.add(StorageManager.STORAGE_POOL_CLIENT_TIMEOUT.key());
+
configValuesForValidation.add(StorageManager.STORAGE_POOL_CLIENT_MAX_CONNECTIONS.key());
}
private void weightBasedParametersForValidation() {
diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
index 5e7be30..9e6cb3f 100644
--- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
+++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
@@ -3130,6 +3130,7 @@ public class StorageManagerImpl extends ManagerBase
implements StorageManager, C
MaxNumberOfManagedClusteredFileSystems,
STORAGE_POOL_DISK_WAIT,
STORAGE_POOL_CLIENT_TIMEOUT,
+ STORAGE_POOL_CLIENT_MAX_CONNECTIONS,
PRIMARY_STORAGE_DOWNLOAD_WAIT,
SecStorageMaxMigrateSessions,
MaxDataMigrationWaitTime
diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
index a2b2a49..bdad40f 100644
--- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
+++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
@@ -1485,6 +1485,13 @@ public class VolumeApiServiceImpl extends ManagerBase
implements VolumeApiServic
}
}
+ private void removeVolume(long volumeId) {
+ final VolumeVO volume = _volsDao.findById(volumeId);
+ if (volume != null) {
+ _volsDao.remove(volumeId);
+ }
+ }
+
protected boolean stateTransitTo(Volume vol, Volume.Event event) throws
NoTransitionException {
return _volStateMachine.transitTo(vol, event, null, _volsDao);
}
@@ -1526,6 +1533,7 @@ public class VolumeApiServiceImpl extends ManagerBase
implements VolumeApiServic
}
}
+ removeVolume(volume.getId());
return volume;
}
@@ -1621,6 +1629,9 @@ public class VolumeApiServiceImpl extends ManagerBase
implements VolumeApiServic
if (destPrimaryStorage != null && (volumeToAttach.getState() ==
Volume.State.Allocated || volumeOnSecondary)) {
try {
+ if (volumeOnSecondary && destPrimaryStorage.getPoolType() ==
Storage.StoragePoolType.PowerFlex) {
+ throw new InvalidParameterValueException("Cannot attach
uploaded volume, this operation is unsupported on storage pool type " +
destPrimaryStorage.getPoolType());
+ }
newVolumeOnPrimaryStorage =
_volumeMgr.createVolumeOnPrimaryStorage(vm, volumeToAttach, rootDiskHyperType,
destPrimaryStorage);
} catch (NoTransitionException e) {
s_logger.debug("Failed to create volume on primary storage",
e);