Skip to content

Commit cce01f1

Browse files
PowerFlex/ScaleIO client initialization, authentication and command execution improvements
1 parent 425c4e3 commit cce01f1

File tree

9 files changed

+141
-101
lines changed

9 files changed

+141
-101
lines changed

framework/config/src/main/java/org/apache/cloudstack/framework/config/ConfigKey.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,13 @@ protected T valueInGlobalOrAvailableParentScope(Scope scope, Long id) {
400400
return value();
401401
}
402402

403+
/**
404+
* Use {@link ConfigKey#valueInScope(Scope, Long)} instead.
405+
*/
406+
public T valueInDomain(Long domainId) {
407+
return valueInScope(Scope.Domain, domainId);
408+
}
409+
403410
public T valueInScope(Scope scope, Long id) {
404411
if (id == null) {
405412
return value();

plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptorTest.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ public void testUnprepareStorageClient_MDMNotAdded() {
180180
details.put(ScaleIOGatewayClient.STORAGE_POOL_MDMS, "1.1.1.1,2.2.2.2");
181181
when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl status scini"))).thenReturn(3);
182182
when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl is-enabled scini"))).thenReturn(0);
183-
when(Script.runSimpleBashScript(Mockito.eq("/opt/emc/scaleio/sdc/bin/drv_cfg --query_mdms|grep 1.1.1.1"))).thenReturn("MDM-ID 71fd458f0775010f SDC ID 4421a91a00000000 INSTALLATION ID 204930df2cbcaf8e IPs [0]-3.3.3.3 [1]-4.4.4.4");
183+
when(Script.runSimpleBashScript(Mockito.eq("/opt/emc/scaleio/sdc/bin/drv_cfg --query_mdms|grep 1.1.1.1"), Mockito.eq(ScaleIOUtil.DEFAULT_TIMEOUT_MS)))
184+
.thenReturn("MDM-ID 71fd458f0775010f SDC ID 4421a91a00000000 INSTALLATION ID 204930df2cbcaf8e IPs [0]-3.3.3.3 [1]-4.4.4.4");
184185

185186
Pair<Boolean, String> result = scaleIOStorageAdaptor.unprepareStorageClient(poolUuid, details);
186187

@@ -196,11 +197,11 @@ public void testUnprepareStorageClient_RemoveMDMFailed() {
196197
when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl is-enabled scini"))).thenReturn(0);
197198
when(Script.executeCommand(Mockito.eq("sed -i '/1.1.1.1\\,/d' /etc/emc/scaleio/drv_cfg.txt"))).thenReturn(new Pair<>(null, null));
198199
when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl restart scini"))).thenReturn(0);
199-
when(Script.runSimpleBashScript(Mockito.eq("/opt/emc/scaleio/sdc/bin/drv_cfg --query_mdms|grep 1.1.1.1"))).thenReturn("MDM-ID 71fd458f0775010f SDC ID 4421a91a00000000 INSTALLATION ID 204930df2cbcaf8e IPs [0]-1.1.1.1 [1]-2.2.2.2");
200+
when(Script.runSimpleBashScript(Mockito.eq("/opt/emc/scaleio/sdc/bin/drv_cfg --query_mdms --file /etc/emc/scaleio/drv_cfg.txt|grep 1.1.1.1"), Mockito.eq(ScaleIOUtil.DEFAULT_TIMEOUT_MS)))
201+
.thenReturn("MDM-ID 71fd458f0775010f SDC ID 4421a91a00000000 INSTALLATION ID 204930df2cbcaf8e IPs [0]-1.1.1.1 [1]-2.2.2.2");
200202
when(Script.executeCommand(Mockito.eq("/opt/emc/scaleio/sdc/bin/drv_cfg"))).thenReturn(new Pair<>(null, null));
201203
when(Script.executeCommand(Mockito.eq("/opt/emc/scaleio/sdc/bin/drv_cfg --query_vols"))).thenReturn(new Pair<>("", null));
202204

203-
204205
Pair<Boolean, String> result = scaleIOStorageAdaptor.unprepareStorageClient(poolUuid, details);
205206

206207
Assert.assertFalse(result.first());

plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePoolTest.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,8 @@ public void testSdcIdAttribute() {
9595
details.put(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID, systemId);
9696

9797
try (MockedStatic<Script> ignored = Mockito.mockStatic(Script.class)) {
98-
when(Script.runSimpleBashScript(
99-
"/opt/emc/scaleio/sdc/bin/drv_cfg --query_mdms|grep 218ce1797566a00f|awk '{print $5}'")).thenReturn(
100-
sdcId);
98+
when(Script.runSimpleBashScript(Mockito.eq("/opt/emc/scaleio/sdc/bin/drv_cfg --query_mdms --file /etc/emc/scaleio/drv_cfg.txt|grep 218ce1797566a00f|awk '{print $5}'"), Mockito.eq(ScaleIOUtil.DEFAULT_TIMEOUT_MS)))
99+
.thenReturn(sdcId);
101100

102101
ScaleIOStoragePool pool1 = new ScaleIOStoragePool(uuid, "192.168.1.19", 443, "a519be2f00000000", type,
103102
details, adapter);
@@ -116,10 +115,10 @@ public void testSdcGuidAttribute() {
116115
details.put(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID, systemId);
117116

118117
try (MockedStatic<Script> ignored = Mockito.mockStatic(Script.class)) {
119-
when(Script.runSimpleBashScript(
120-
"/opt/emc/scaleio/sdc/bin/drv_cfg --query_mdms|grep 218ce1797566a00f|awk '{print $5}'")).thenReturn(
121-
null);
122-
when(Script.runSimpleBashScript("/opt/emc/scaleio/sdc/bin/drv_cfg --query_guid")).thenReturn(sdcGuid);
118+
when(Script.runSimpleBashScript(Mockito.eq("/opt/emc/scaleio/sdc/bin/drv_cfg --query_mdms --file /etc/emc/scaleio/drv_cfg.txt|grep 218ce1797566a00f|awk '{print $5}'"), Mockito.eq(ScaleIOUtil.DEFAULT_TIMEOUT_MS)))
119+
.thenReturn(null);
120+
when(Script.runSimpleBashScript(Mockito.eq("/opt/emc/scaleio/sdc/bin/drv_cfg --query_guid --file /etc/emc/scaleio/drv_cfg.txt"), Mockito.eq(ScaleIOUtil.DEFAULT_TIMEOUT_MS)))
121+
.thenReturn(sdcGuid);
123122

124123
ScaleIOStoragePool pool1 = new ScaleIOStoragePool(uuid, "192.168.1.19", 443, "a519be2f00000000", type,
125124
details, adapter);

plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientConnectionPool.java

Lines changed: 48 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@
1717

1818
package org.apache.cloudstack.storage.datastore.client;
1919

20-
import java.net.URISyntaxException;
21-
import java.security.KeyManagementException;
22-
import java.security.NoSuchAlgorithmException;
20+
import java.util.Map;
21+
import java.util.Optional;
2322
import java.util.concurrent.ConcurrentHashMap;
2423

2524
import com.cloud.storage.StoragePool;
25+
import com.cloud.utils.exception.CloudRuntimeException;
2626
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
2727
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO;
2828
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
@@ -36,9 +36,9 @@
3636
public class ScaleIOGatewayClientConnectionPool {
3737
protected Logger logger = LogManager.getLogger(getClass());
3838

39-
private ConcurrentHashMap<Long, ScaleIOGatewayClient> gatewayClients;
40-
39+
private Map<Long, ScaleIOGatewayClient> gatewayClients;
4140
private static final ScaleIOGatewayClientConnectionPool instance;
41+
private final Object lock = new Object();
4242

4343
static {
4444
instance = new ScaleIOGatewayClientConnectionPool();
@@ -49,69 +49,76 @@ public static ScaleIOGatewayClientConnectionPool getInstance() {
4949
}
5050

5151
private ScaleIOGatewayClientConnectionPool() {
52-
gatewayClients = new ConcurrentHashMap<Long, ScaleIOGatewayClient>();
52+
gatewayClients = new ConcurrentHashMap<>();
5353
}
5454

5555
public ScaleIOGatewayClient getClient(StoragePool storagePool,
56-
StoragePoolDetailsDao storagePoolDetailsDao)
57-
throws NoSuchAlgorithmException, KeyManagementException, URISyntaxException {
56+
StoragePoolDetailsDao storagePoolDetailsDao) {
5857
return getClient(storagePool.getId(), storagePool.getUuid(), storagePoolDetailsDao);
5958
}
6059

6160

6261
public ScaleIOGatewayClient getClient(DataStore dataStore,
63-
StoragePoolDetailsDao storagePoolDetailsDao)
64-
throws NoSuchAlgorithmException, KeyManagementException, URISyntaxException {
62+
StoragePoolDetailsDao storagePoolDetailsDao) {
6563
return getClient(dataStore.getId(), dataStore.getUuid(), storagePoolDetailsDao);
6664
}
6765

68-
6966
private ScaleIOGatewayClient getClient(Long storagePoolId, String storagePoolUuid,
70-
StoragePoolDetailsDao storagePoolDetailsDao)
71-
throws NoSuchAlgorithmException, KeyManagementException, URISyntaxException {
67+
StoragePoolDetailsDao storagePoolDetailsDao) {
7268

7369
Preconditions.checkArgument(storagePoolId != null && storagePoolId > 0,
7470
"Invalid storage pool id");
7571

76-
ScaleIOGatewayClient client = null;
77-
synchronized (gatewayClients) {
78-
client = gatewayClients.get(storagePoolId);
79-
if (client == null) {
80-
String url = null;
81-
StoragePoolDetailVO urlDetail = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_ENDPOINT);
82-
if (urlDetail != null) {
83-
url = urlDetail.getValue();
84-
}
85-
String username = null;
86-
StoragePoolDetailVO encryptedUsernameDetail = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_USERNAME);
87-
if (encryptedUsernameDetail != null) {
88-
final String encryptedUsername = encryptedUsernameDetail.getValue();
89-
username = DBEncryptionUtil.decrypt(encryptedUsername);
72+
logger.debug("Getting ScaleIO client for {} ({})", storagePoolId, storagePoolUuid);
73+
74+
ScaleIOGatewayClient client = gatewayClients.get(storagePoolId);
75+
if (client == null) {
76+
logger.debug("Before acquiring lock to create ScaleIO client for {} ({})", storagePoolId, storagePoolUuid);
77+
synchronized (lock) {
78+
logger.debug("Acquired lock to create ScaleIO client for {} ({})", storagePoolId, storagePoolUuid);
79+
client = gatewayClients.get(storagePoolId);
80+
if (client == null) {
81+
logger.debug("Initializing ScaleIO client for {} ({})", storagePoolId, storagePoolUuid);
82+
83+
String url = Optional.ofNullable(storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_ENDPOINT))
84+
.map(StoragePoolDetailVO::getValue)
85+
.orElse(null);
86+
87+
String username = Optional.ofNullable(storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_USERNAME))
88+
.map(StoragePoolDetailVO::getValue)
89+
.map(DBEncryptionUtil::decrypt)
90+
.orElse(null);
91+
92+
String password = Optional.ofNullable(storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_PASSWORD))
93+
.map(StoragePoolDetailVO::getValue)
94+
.map(DBEncryptionUtil::decrypt)
95+
.orElse(null);
96+
97+
int clientTimeout = StorageManager.STORAGE_POOL_CLIENT_TIMEOUT.valueIn(storagePoolId);
98+
int clientMaxConnections = StorageManager.STORAGE_POOL_CLIENT_MAX_CONNECTIONS.valueIn(storagePoolId);
99+
100+
try {
101+
client = new ScaleIOGatewayClientImpl(url, username, password, false, clientTimeout, clientMaxConnections);
102+
logger.debug("Created ScaleIO client for the storage pool [id: {}, uuid: {}]", storagePoolId, storagePoolUuid);
103+
gatewayClients.put(storagePoolId, client);
104+
} catch (Exception e) {
105+
String msg = String.format("Failed to create ScaleIO client for the storage pool [id: %d, uuid: %s]", storagePoolId, storagePoolUuid);
106+
throw new CloudRuntimeException(msg, e);
107+
}
90108
}
91-
String password = null;
92-
StoragePoolDetailVO encryptedPasswordDetail = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_PASSWORD);
93-
if (encryptedPasswordDetail != null) {
94-
final String encryptedPassword = encryptedPasswordDetail.getValue();
95-
password = DBEncryptionUtil.decrypt(encryptedPassword);
96-
}
97-
final int clientTimeout = StorageManager.STORAGE_POOL_CLIENT_TIMEOUT.valueIn(storagePoolId);
98-
final int clientMaxConnections = StorageManager.STORAGE_POOL_CLIENT_MAX_CONNECTIONS.valueIn(storagePoolId);
99-
100-
client = new ScaleIOGatewayClientImpl(url, username, password, false, clientTimeout, clientMaxConnections);
101-
gatewayClients.put(storagePoolId, client);
102-
logger.debug("Added gateway client for the storage pool [id: {}, uuid: {}]", storagePoolId, storagePoolUuid);
103109
}
104110
}
105111

112+
logger.debug("Returning ScaleIO client for {} ({})", storagePoolId, storagePoolUuid);
106113
return client;
107114
}
108115

109116
public boolean removeClient(DataStore dataStore) {
110117
Preconditions.checkArgument(dataStore != null && dataStore.getId() > 0,
111118
"Invalid storage pool id");
112119

113-
ScaleIOGatewayClient client = null;
114-
synchronized (gatewayClients) {
120+
ScaleIOGatewayClient client;
121+
synchronized (lock) {
115122
client = gatewayClients.remove(dataStore.getId());
116123
}
117124

plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public class ScaleIOGatewayClientImpl implements ScaleIOGatewayClient {
9292

9393
private String username;
9494
private String password;
95-
private String sessionKey = null;
95+
private String sessionKey;
9696

9797
// The session token is valid for 8 hours from the time it was created, unless there has been no activity for 10 minutes
9898
// Reference: https://cpsdocs.dellemc.com/bundle/PF_REST_API_RG/page/GUID-92430F19-9F44-42B6-B898-87D5307AE59B.html
@@ -102,7 +102,7 @@ public class ScaleIOGatewayClientImpl implements ScaleIOGatewayClient {
102102
private static final long MAX_IDLE_TIME_IN_MILLISECS = MAX_IDLE_TIME_IN_MINS * 60 * 1000;
103103
private static final long BUFFER_TIME_IN_MILLISECS = 30 * 1000; // keep 30 secs buffer before the expiration (to avoid any last-minute operations)
104104

105-
private boolean authenticating = false;
105+
private volatile boolean authenticating = false;
106106
private long createTime = 0;
107107
private long lastUsedTime = 0;
108108

@@ -142,7 +142,6 @@ public ScaleIOGatewayClientImpl(final String url, final String username, final S
142142
this.username = username;
143143
this.password = password;
144144

145-
authenticate();
146145
logger.debug("API client for the PowerFlex gateway " + apiURI.getHost() + " is created successfully, with max connections: "
147146
+ maxConnections + " and timeout: " + timeout + " secs");
148147
}
@@ -181,7 +180,7 @@ private synchronized void authenticate() {
181180
long now = System.currentTimeMillis();
182181
createTime = lastUsedTime = now;
183182
} catch (final IOException e) {
184-
logger.error("Failed to authenticate PowerFlex API Gateway " + apiURI.getHost() + " due to: " + e.getMessage() + getConnectionManagerStats());
183+
logger.error("Failed to authenticate PowerFlex API Gateway " + apiURI.getHost() + " due to: " + e.getMessage() + getConnectionManagerStats(), e);
185184
throw new CloudRuntimeException("Failed to authenticate PowerFlex API Gateway " + apiURI.getHost() + " due to: " + e.getMessage());
186185
} finally {
187186
authenticating = false;
@@ -199,6 +198,10 @@ private synchronized void renewClientSessionOnExpiry() {
199198
}
200199

201200
private boolean isSessionExpired() {
201+
if (sessionKey == null) {
202+
logger.debug("Session never created for the Gateway " + apiURI.getHost());
203+
return true;
204+
}
202205
long now = System.currentTimeMillis() + BUFFER_TIME_IN_MILLISECS;
203206
if ((now - createTime) > MAX_VALID_SESSION_TIME_IN_MILLISECS) {
204207
logger.debug("Session expired for the Gateway " + apiURI.getHost() + ", token is invalid after " + MAX_VALID_SESSION_TIME_IN_HRS
@@ -281,7 +284,11 @@ private <T> T get(final String path, final Class<T> type, final boolean renewAnd
281284
HttpResponse response = null;
282285
boolean responseConsumed = false;
283286
try {
284-
while (authenticating); // wait for authentication request (if any) to complete (and to pick the new session key)
287+
while (authenticating) { // wait for authentication request (if any)
288+
// to complete (and to pick the new session key)
289+
Thread.yield();
290+
}
291+
285292
final HttpGet request = new HttpGet(apiURI.toString() + path);
286293
request.setHeader(HttpHeaders.AUTHORIZATION, "Basic " + Base64.getEncoder().encodeToString((this.username + ":" + this.sessionKey).getBytes()));
287294
logger.debug("Sending GET request: " + request.toString());
@@ -316,7 +323,10 @@ private <T> T post(final String path, final Object obj, final Class<T> type, fin
316323
HttpResponse response = null;
317324
boolean responseConsumed = false;
318325
try {
319-
while (authenticating); // wait for authentication request (if any) to complete (and to pick the new session key)
326+
while (authenticating) { // wait for authentication request (if any)
327+
// to complete (and to pick the new session key)
328+
Thread.yield();
329+
}
320330
final HttpPost request = new HttpPost(apiURI.toString() + path);
321331
request.setHeader(HttpHeaders.AUTHORIZATION, "Basic " + Base64.getEncoder().encodeToString((this.username + ":" + this.sessionKey).getBytes()));
322332
request.setHeader("content-type", "application/json");

plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/driver/ScaleIOPrimaryDataStoreDriver.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,15 @@ public ScaleIOPrimaryDataStoreDriver() {
153153
sdcManager = new ScaleIOSDCManagerImpl();
154154
}
155155

156-
ScaleIOGatewayClient getScaleIOClient(final StoragePool storagePool) throws Exception {
156+
ScaleIOGatewayClient getScaleIOClient(final StoragePool storagePool) {
157157
return ScaleIOGatewayClientConnectionPool.getInstance().getClient(storagePool, storagePoolDetailsDao);
158158
}
159159

160-
ScaleIOGatewayClient getScaleIOClient(final DataStore dataStore) throws Exception {
160+
ScaleIOGatewayClient getScaleIOClient(final DataStore dataStore) {
161161
return ScaleIOGatewayClientConnectionPool.getInstance().getClient(dataStore, storagePoolDetailsDao);
162162
}
163163

164-
private boolean setVolumeLimitsOnSDC(VolumeVO volume, Host host, DataStore dataStore, Long iopsLimit, Long bandwidthLimitInKbps) throws Exception {
164+
private boolean setVolumeLimitsOnSDC(VolumeVO volume, Host host, DataStore dataStore, Long iopsLimit, Long bandwidthLimitInKbps) {
165165
sdcManager = ComponentContext.inject(sdcManager);
166166
final String sdcId = sdcManager.prepareSDC(host, dataStore);
167167
if (StringUtils.isBlank(sdcId)) {
@@ -173,7 +173,7 @@ private boolean setVolumeLimitsOnSDC(VolumeVO volume, Host host, DataStore dataS
173173
return client.mapVolumeToSdcWithLimits(ScaleIOUtil.getVolumePath(volume.getPath()), sdcId, iopsLimit, bandwidthLimitInKbps);
174174
}
175175

176-
private boolean setVolumeLimitsFromDetails(VolumeVO volume, Host host, DataStore dataStore) throws Exception {
176+
private boolean setVolumeLimitsFromDetails(VolumeVO volume, Host host, DataStore dataStore) {
177177
Long bandwidthLimitInKbps = 0L; // Unlimited
178178
// Check Bandwidth Limit parameter in volume details
179179
final VolumeDetailVO bandwidthVolumeDetail = volumeDetailsDao.findDetail(volume.getId(), Volume.BANDWIDTH_LIMIT_IN_MBPS);
@@ -997,7 +997,7 @@ private Host findEndpointForVolumeOperation(DataObject srcData) {
997997
return host;
998998
}
999999

1000-
public void updateSnapshotsAfterCopyVolume(DataObject srcData, DataObject destData) throws Exception {
1000+
public void updateSnapshotsAfterCopyVolume(DataObject srcData, DataObject destData) {
10011001
final long srcVolumeId = srcData.getId();
10021002
DataStore srcStore = srcData.getDataStore();
10031003
final ScaleIOGatewayClient client = getScaleIOClient(srcStore);

0 commit comments

Comments
 (0)