Skip to content

Commit 2ca1b47

Browse files
PowerFlex/ScaleIO SDC client connection improvements (#9268)
* Mitigation for non-scalable Powerflex/ScaleIO clients - Added ScaleIOSDCManager to manage SDC connections, checks clients limit, prepare and unprepare SDC on the hosts. - Added commands for prepare and unprepare storage clients to prepare/start and stop SDC service respectively on the hosts. - Introduced config 'storage.pool.connected.clients.limit' at storage level for client limits, currently support for Powerflex only. * tests issue fixed * refactor / improvements * lock with powerflex systemid while checking connections limit * updated powerflex systemid lock to hold till sdc preparation * Added custom stats support for storage pool, through listStoragePools API * code improvements, and unit tests * unit tests fixes * Update config 'storage.pool.connected.clients.limit' to dynamic, and some improvements * Stop SDC on host after migration if no volumes mapped to host * Wait for SDC to connect after scini service start, and some log improvements * Do not throw exception (log it) when SDC is not connected while revoking access for the powerflex volume * some log improvements
1 parent 814c8b6 commit 2ca1b47

File tree

35 files changed

+1431
-118
lines changed

35 files changed

+1431
-118
lines changed

api/src/main/java/org/apache/cloudstack/api/ApiConstants.java

+1
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,7 @@ public class ApiConstants {
439439
public static final String STORAGE_POLICY = "storagepolicy";
440440
public static final String STORAGE_MOTION_ENABLED = "storagemotionenabled";
441441
public static final String STORAGE_CAPABILITIES = "storagecapabilities";
442+
public static final String STORAGE_CUSTOM_STATS = "storagecustomstats";
442443
public static final String SUBNET = "subnet";
443444
public static final String OWNER = "owner";
444445
public static final String SWAP_OWNER = "swapowner";

api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStoragePoolsCmd.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ public class ListStoragePoolsCmd extends BaseListCmd {
7474
@Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "host ID of the storage pools")
7575
private Long hostId;
7676

77-
77+
@Parameter(name = ApiConstants.STORAGE_CUSTOM_STATS, type = CommandType.BOOLEAN, description = "If true, lists the custom stats of the storage pool", since = "4.18.1")
78+
private Boolean customStats;
7879
/////////////////////////////////////////////////////
7980
/////////////////// Accessors ///////////////////////
8081
/////////////////////////////////////////////////////
@@ -131,6 +132,10 @@ public void setScope(String scope) {
131132
this.scope = scope;
132133
}
133134

135+
public Boolean getCustomStats() {
136+
return customStats != null && customStats;
137+
}
138+
134139
/////////////////////////////////////////////////////
135140
/////////////// API Implementation///////////////////
136141
/////////////////////////////////////////////////////

api/src/main/java/org/apache/cloudstack/api/response/StoragePoolResponse.java

+12
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ public class StoragePoolResponse extends BaseResponseWithAnnotations {
9797
@Param(description = "total min IOPS currently in use by volumes")
9898
private Long allocatedIops;
9999

100+
@SerializedName(ApiConstants.STORAGE_CUSTOM_STATS)
101+
@Param(description = "the storage pool custom stats", since = "4.18.1")
102+
private Map<String, String> customStats;
103+
100104
@SerializedName("tags")
101105
@Param(description = "the tags for the storage pool")
102106
private String tags;
@@ -304,6 +308,14 @@ public void setAllocatedIops(Long allocatedIops) {
304308
this.allocatedIops = allocatedIops;
305309
}
306310

311+
public Map<String, String> getCustomStats() {
312+
return customStats;
313+
}
314+
315+
public void setCustomStats(Map<String, String> customStats) {
316+
this.customStats = customStats;
317+
}
318+
307319
public String getTags() {
308320
return tags;
309321
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
20+
package com.cloud.agent.api;
21+
22+
import java.util.Map;
23+
24+
public class PrepareStorageClientAnswer extends Answer {
25+
Map<String, String> detailsMap;
26+
27+
public PrepareStorageClientAnswer() {
28+
super();
29+
}
30+
31+
public PrepareStorageClientAnswer(Command command, boolean success, Map<String, String> detailsMap) {
32+
super(command, success, "");
33+
this.detailsMap = detailsMap;
34+
}
35+
36+
public PrepareStorageClientAnswer(Command command, boolean success, String details) {
37+
super(command, success, details);
38+
}
39+
40+
public Map<String, String> getDetailsMap() {
41+
return detailsMap;
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
20+
package com.cloud.agent.api;
21+
22+
import java.util.Map;
23+
24+
import com.cloud.storage.Storage.StoragePoolType;
25+
26+
public class PrepareStorageClientCommand extends Command {
27+
private StoragePoolType poolType;
28+
private String poolUuid;
29+
private Map<String, String> details;
30+
31+
public PrepareStorageClientCommand() {
32+
}
33+
34+
public PrepareStorageClientCommand(StoragePoolType poolType, String poolUuid, Map<String, String> details) {
35+
this.poolType = poolType;
36+
this.poolUuid = poolUuid;
37+
this.details = details;
38+
}
39+
40+
@Override
41+
public boolean executeInSequence() {
42+
return false;
43+
}
44+
45+
public StoragePoolType getPoolType() {
46+
return poolType;
47+
}
48+
49+
public String getPoolUuid() {
50+
return poolUuid;
51+
}
52+
53+
public Map<String, String> getDetails() {
54+
return details;
55+
}
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
20+
package com.cloud.agent.api;
21+
22+
public class UnprepareStorageClientAnswer extends Answer {
23+
public UnprepareStorageClientAnswer() {
24+
super();
25+
}
26+
27+
public UnprepareStorageClientAnswer(Command command, boolean success) {
28+
super(command, success, "");
29+
}
30+
31+
public UnprepareStorageClientAnswer(Command command, boolean success, String details) {
32+
super(command, success, details);
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
20+
package com.cloud.agent.api;
21+
22+
import com.cloud.storage.Storage.StoragePoolType;
23+
24+
public class UnprepareStorageClientCommand extends Command {
25+
private StoragePoolType poolType;
26+
private String poolUuid;
27+
28+
public UnprepareStorageClientCommand() {
29+
}
30+
31+
public UnprepareStorageClientCommand(StoragePoolType poolType, String poolUuid) {
32+
this.poolType = poolType;
33+
this.poolUuid = poolUuid;
34+
}
35+
36+
@Override
37+
public boolean executeInSequence() {
38+
return false;
39+
}
40+
41+
public StoragePoolType getPoolType() {
42+
return poolType;
43+
}
44+
45+
public String getPoolUuid() {
46+
return poolUuid;
47+
}
48+
}

engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java

+26
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
*/
1919
package org.apache.cloudstack.engine.subsystem.api.storage;
2020

21+
import java.util.Map;
22+
2123
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
2224
import org.apache.cloudstack.storage.command.CommandResult;
2325

@@ -86,6 +88,22 @@ default boolean requiresAccessForMigration(DataObject dataObject) {
8688
*/
8789
boolean canProvideStorageStats();
8890

91+
/**
92+
* intended for managed storage
93+
* returns true if the storage can provide its custom stats
94+
*/
95+
default boolean poolProvidesCustomStorageStats() {
96+
return false;
97+
}
98+
99+
/**
100+
* intended for managed storage
101+
* returns the custom stats if the storage can provide them
102+
*/
103+
default Map<String, String> getCustomStorageStats(StoragePool pool) {
104+
return null;
105+
}
106+
89107
/**
90108
* intended for managed storage
91109
* returns the total capacity and used size in bytes
@@ -110,6 +128,14 @@ default boolean requiresAccessForMigration(DataObject dataObject) {
110128
*/
111129
boolean canHostAccessStoragePool(Host host, StoragePool pool);
112130

131+
/**
132+
* intended for managed storage
133+
* returns true if the host can prepare storage client to provide access the storage pool
134+
*/
135+
default boolean canHostPrepareStoragePoolAccess(Host host, StoragePool pool) {
136+
return false;
137+
}
138+
113139
/**
114140
* Used by storage pools which want to keep VMs' information
115141
* @return true if additional VM info is needed (intended for storage pools).

engine/components-api/src/main/java/com/cloud/storage/StorageManager.java

+18-3
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public interface StorageManager extends StorageService {
118118
"storage.pool.disk.wait",
119119
"Storage",
120120
"60",
121-
"Timeout (in secs) for the storage pool disk (of managed pool) to become available in the host. Currently only supported for PowerFlex.",
121+
"Timeout (in secs) for the storage pool disk (of managed pool) to become available in the host. Currently supported for PowerFlex only.",
122122
true,
123123
ConfigKey.Scope.StoragePool,
124124
null);
@@ -127,7 +127,7 @@ public interface StorageManager extends StorageService {
127127
"storage.pool.client.timeout",
128128
"Storage",
129129
"60",
130-
"Timeout (in secs) for the storage pool client connection timeout (for managed pools). Currently only supported for PowerFlex.",
130+
"Timeout (in secs) for the API client connection timeout of storage pool (for managed pools). Currently supported for PowerFlex only.",
131131
false,
132132
ConfigKey.Scope.StoragePool,
133133
null);
@@ -136,11 +136,20 @@ public interface StorageManager extends StorageService {
136136
"storage.pool.client.max.connections",
137137
"Storage",
138138
"100",
139-
"Maximum connections for the storage pool client (for managed pools). Currently only supported for PowerFlex.",
139+
"Maximum connections for the API client of storage pool (for managed pools). Currently supported for PowerFlex only.",
140140
false,
141141
ConfigKey.Scope.StoragePool,
142142
null);
143143

144+
ConfigKey<Integer> STORAGE_POOL_CONNECTED_CLIENTS_LIMIT = new ConfigKey<>(Integer.class,
145+
"storage.pool.connected.clients.limit",
146+
"Storage",
147+
"-1",
148+
"Maximum connected storage pool clients supported for the storage (for managed pools), <= 0 for unlimited (default: -1). Currently supported for PowerFlex only.",
149+
true,
150+
ConfigKey.Scope.StoragePool,
151+
null);
152+
144153
ConfigKey<String> STORAGE_POOL_IO_POLICY = new ConfigKey<>(String.class,
145154
"kvm.storage.pool.io.policy",
146155
"Storage",
@@ -252,6 +261,10 @@ static Boolean getFullCloneConfiguration(Long storeId) {
252261

253262
boolean canPoolProvideStorageStats(StoragePool pool);
254263

264+
boolean poolProvidesCustomStorageStats(StoragePool pool);
265+
266+
Map<String, String> getCustomStorageStats(StoragePool pool);
267+
255268
/**
256269
* Checks if a host has running VMs that are using its local storage pool.
257270
* @return true if local storage is active on the host
@@ -286,6 +299,8 @@ static Boolean getFullCloneConfiguration(Long storeId) {
286299

287300
boolean canHostAccessStoragePool(Host host, StoragePool pool);
288301

302+
boolean canHostPrepareStoragePoolAccess(Host host, StoragePool pool);
303+
289304
Host getHost(long hostId);
290305

291306
Host updateSecondaryStorage(long secStorageId, String newUrl);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
20+
package com.cloud.hypervisor.kvm.resource.wrapper;
21+
22+
import java.util.Map;
23+
24+
import org.apache.log4j.Logger;
25+
26+
import com.cloud.agent.api.Answer;
27+
import com.cloud.agent.api.PrepareStorageClientAnswer;
28+
import com.cloud.agent.api.PrepareStorageClientCommand;
29+
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
30+
import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
31+
import com.cloud.resource.CommandWrapper;
32+
import com.cloud.resource.ResourceWrapper;
33+
import com.cloud.utils.Ternary;
34+
35+
@ResourceWrapper(handles = PrepareStorageClientCommand.class)
36+
public class LibvirtPrepareStorageClientCommandWrapper extends CommandWrapper<PrepareStorageClientCommand, Answer, LibvirtComputingResource> {
37+
38+
private static final Logger s_logger = Logger.getLogger(LibvirtPrepareStorageClientCommandWrapper.class);
39+
40+
@Override
41+
public Answer execute(PrepareStorageClientCommand cmd, LibvirtComputingResource libvirtComputingResource) {
42+
final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr();
43+
Ternary<Boolean, Map<String, String>, String> prepareStorageClientResult = storagePoolMgr.prepareStorageClient(cmd.getPoolType(), cmd.getPoolUuid(), cmd.getDetails());
44+
if (!prepareStorageClientResult.first()) {
45+
String msg = prepareStorageClientResult.third();
46+
s_logger.debug("Unable to prepare storage client, due to: " + msg);
47+
return new PrepareStorageClientAnswer(cmd, false, msg);
48+
}
49+
Map<String, String> details = prepareStorageClientResult.second();
50+
return new PrepareStorageClientAnswer(cmd, true, details);
51+
}
52+
}

0 commit comments

Comments
 (0)