Skip to content

Commit 60c9c9a

Browse files
committed
Merge remote-tracking branch 'origin/4.19'
Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
2 parents 3915232 + a851ee3 commit 60c9c9a

15 files changed

Lines changed: 145 additions & 40 deletions

File tree

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ public class StoragePoolResponse extends BaseResponseWithAnnotations {
141141
@Param(description = "the storage pool capabilities")
142142
private Map<String, String> caps;
143143

144+
@SerializedName(ApiConstants.MANAGED)
145+
@Param(description = "whether this pool is managed or not")
146+
private Boolean managed;
147+
144148
public Map<String, String> getCaps() {
145149
return caps;
146150
}
@@ -383,4 +387,12 @@ public Boolean getTagARule() {
383387
public void setTagARule(Boolean tagARule) {
384388
isTagARule = tagARule;
385389
}
390+
391+
public Boolean getManaged() {
392+
return managed;
393+
}
394+
395+
public void setManaged(Boolean managed) {
396+
this.managed = managed;
397+
}
386398
}

engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/TemplateServiceImpl.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -533,11 +533,6 @@ public void handleTemplateSync(DataStore store) {
533533
logger.info("Skip downloading template " + tmplt.getUniqueName() + " since no url is specified.");
534534
continue;
535535
}
536-
// if this is private template, skip sync to a new image store
537-
if (isSkipTemplateStoreDownload(tmplt, zoneId)) {
538-
logger.info("Skip sync downloading private template " + tmplt.getUniqueName() + " to a new image store");
539-
continue;
540-
}
541536

542537
// if this is a region store, and there is already an DOWNLOADED entry there without install_path information, which
543538
// means that this is a duplicate entry from migration of previous NFS to staging.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Changelog
2+
3+
All notable changes to Linstor CloudStack plugin will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [2024-08-27]
9+
10+
### Changed
11+
12+
- Allow two primaries(+protocol c) is now set on resource-connection level instead of rd

plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// under the License.
1717
package com.cloud.hypervisor.kvm.storage;
1818

19+
import java.util.ArrayList;
1920
import java.util.Collections;
2021
import java.util.HashMap;
2122
import java.util.List;
@@ -26,6 +27,7 @@
2627

2728
import com.cloud.storage.Storage;
2829
import com.cloud.utils.exception.CloudRuntimeException;
30+
2931
import org.apache.cloudstack.storage.datastore.util.LinstorUtil;
3032
import org.apache.cloudstack.utils.qemu.QemuImg;
3133
import org.apache.cloudstack.utils.qemu.QemuImgException;
@@ -44,8 +46,8 @@
4446
import com.linbit.linstor.api.model.Properties;
4547
import com.linbit.linstor.api.model.ProviderKind;
4648
import com.linbit.linstor.api.model.Resource;
49+
import com.linbit.linstor.api.model.ResourceConnectionModify;
4750
import com.linbit.linstor.api.model.ResourceDefinition;
48-
import com.linbit.linstor.api.model.ResourceDefinitionModify;
4951
import com.linbit.linstor.api.model.ResourceGroupSpawn;
5052
import com.linbit.linstor.api.model.ResourceMakeAvailable;
5153
import com.linbit.linstor.api.model.ResourceWithVolumes;
@@ -242,15 +244,19 @@ public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, Qemu
242244
* @throws ApiException if any problem connecting to the Linstor controller
243245
*/
244246
private void allow2PrimariesIfInUse(DevelopersApi api, String rscName) throws ApiException {
245-
if (LinstorUtil.isResourceInUse(api, rscName)) {
247+
String inUseNode = LinstorUtil.isResourceInUse(api, rscName);
248+
if (inUseNode != null && !inUseNode.equalsIgnoreCase(localNodeName)) {
246249
// allow 2 primaries for live migration, should be removed by disconnect on the other end
247-
ResourceDefinitionModify rdm = new ResourceDefinitionModify();
250+
ResourceConnectionModify rcm = new ResourceConnectionModify();
248251
Properties props = new Properties();
249252
props.put("DrbdOptions/Net/allow-two-primaries", "yes");
250-
rdm.setOverrideProps(props);
251-
ApiCallRcList answers = api.resourceDefinitionModify(rscName, rdm);
253+
props.put("DrbdOptions/Net/protocol", "C");
254+
rcm.setOverrideProps(props);
255+
ApiCallRcList answers = api.resourceConnectionModify(rscName, inUseNode, localNodeName, rcm);
252256
if (answers.hasError()) {
253-
logger.error("Unable to set 'allow-two-primaries' on {} ", rscName);
257+
logger.error(String.format(
258+
"Unable to set protocol C and 'allow-two-primaries' on %s/%s/%s",
259+
inUseNode, localNodeName, rscName));
254260
// do not fail here as adding allow-two-primaries property is only a problem while live migrating
255261
}
256262
}
@@ -290,6 +296,23 @@ public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map<S
290296
return true;
291297
}
292298

299+
private void removeTwoPrimariesRcProps(DevelopersApi api, String inUseNode, String rscName) throws ApiException {
300+
ResourceConnectionModify rcm = new ResourceConnectionModify();
301+
List<String> deleteProps = new ArrayList<>();
302+
deleteProps.add("DrbdOptions/Net/allow-two-primaries");
303+
deleteProps.add("DrbdOptions/Net/protocol");
304+
rcm.deleteProps(deleteProps);
305+
ApiCallRcList answers = api.resourceConnectionModify(rscName, localNodeName, inUseNode, rcm);
306+
if (answers.hasError()) {
307+
logger.error(
308+
String.format("Failed to remove 'protocol' and 'allow-two-primaries' on %s/%s/%s: %s",
309+
localNodeName,
310+
inUseNode,
311+
rscName, LinstorUtil.getBestErrorMessage(answers)));
312+
// do not fail here as removing allow-two-primaries property isn't fatal
313+
}
314+
}
315+
293316
private boolean tryDisconnectLinstor(String volumePath, KVMStoragePool pool)
294317
{
295318
if (volumePath == null) {
@@ -319,27 +342,25 @@ private boolean tryDisconnectLinstor(String volumePath, KVMStoragePool pool)
319342

320343

321344
if (optRsc.isPresent()) {
345+
Resource rsc = optRsc.get();
322346
try {
323-
Resource rsc = optRsc.get();
347+
String inUseNode = LinstorUtil.isResourceInUse(api, rsc.getName());
348+
if (inUseNode != null && !inUseNode.equalsIgnoreCase(localNodeName)) {
349+
removeTwoPrimariesRcProps(api, inUseNode, rsc.getName());
350+
}
351+
} catch (ApiException apiEx) {
352+
logger.error(apiEx.getBestMessage());
353+
// do not fail here as removing allow-two-primaries property or deleting diskless isn't fatal
354+
}
324355

356+
try {
325357
// if diskless resource remove it, in the worst case it will be transformed to a tiebreaker
326358
if (rsc.getFlags() != null &&
327359
rsc.getFlags().contains(ApiConsts.FLAG_DRBD_DISKLESS) &&
328360
!rsc.getFlags().contains(ApiConsts.FLAG_TIE_BREAKER)) {
329361
ApiCallRcList delAnswers = api.resourceDelete(rsc.getName(), localNodeName);
330362
logLinstorAnswers(delAnswers);
331363
}
332-
333-
// remove allow-two-primaries
334-
ResourceDefinitionModify rdm = new ResourceDefinitionModify();
335-
rdm.deleteProps(Collections.singletonList("DrbdOptions/Net/allow-two-primaries"));
336-
ApiCallRcList answers = api.resourceDefinitionModify(rsc.getName(), rdm);
337-
if (answers.hasError()) {
338-
logger.error(
339-
String.format("Failed to remove 'allow-two-primaries' on %s: %s",
340-
rsc.getName(), LinstorUtil.getBestErrorMessage(answers)));
341-
// do not fail here as removing allow-two-primaries property isn't fatal
342-
}
343364
} catch (ApiException apiEx) {
344365
logger.error(apiEx.getBestMessage());
345366
// do not fail here as removing allow-two-primaries property or deleting diskless isn't fatal

plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,17 +192,20 @@ public static long getCapacityBytes(String linstorUrl, String rscGroupName) {
192192
*
193193
* @param api developer api object to use
194194
* @param rscName resource name to check in use state.
195-
* @return True if a resource found that is in use(primary) state, else false.
195+
* @return NodeName where the resource is inUse, if not in use `null`
196196
* @throws ApiException forwards api errors
197197
*/
198-
public static boolean isResourceInUse(DevelopersApi api, String rscName) throws ApiException {
198+
public static String isResourceInUse(DevelopersApi api, String rscName) throws ApiException {
199199
List<Resource> rscs = api.resourceList(rscName, null, null);
200200
if (rscs != null) {
201201
return rscs.stream()
202-
.anyMatch(rsc -> rsc.getState() != null && Boolean.TRUE.equals(rsc.getState().isInUse()));
202+
.filter(rsc -> rsc.getState() != null && Boolean.TRUE.equals(rsc.getState().isInUse()))
203+
.map(Resource::getNodeName)
204+
.findFirst()
205+
.orElse(null);
203206
}
204207
LOGGER.error("isResourceInUse: null returned from resourceList");
205-
return false;
208+
return null;
206209
}
207210

208211
/**

server/src/main/java/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ public StoragePoolResponse newStoragePoolResponse(StoragePoolJoinVO pool, boolea
172172
poolResponse.setTags(pool.getTag());
173173
poolResponse.setIsTagARule(pool.getIsTagARule());
174174
poolResponse.setOverProvisionFactor(Double.toString(CapacityManager.StorageOverprovisioningFactor.valueIn(pool.getId())));
175+
poolResponse.setManaged(storagePool.isManaged());
175176

176177
// set async job
177178
if (pool.getJobId() != null) {
@@ -204,6 +205,7 @@ public StoragePoolResponse setStoragePoolResponse(StoragePoolResponse response,
204205

205206
@Override
206207
public StoragePoolResponse newStoragePoolForMigrationResponse(StoragePoolJoinVO pool) {
208+
StoragePool storagePool = storagePoolDao.findById(pool.getId());
207209
StoragePoolResponse poolResponse = new StoragePoolResponse();
208210
poolResponse.setId(pool.getUuid());
209211
poolResponse.setName(pool.getName());
@@ -230,6 +232,17 @@ public StoragePoolResponse newStoragePoolForMigrationResponse(StoragePoolJoinVO
230232
poolResponse.setDiskSizeTotal(pool.getCapacityBytes());
231233
poolResponse.setDiskSizeAllocated(allocatedSize);
232234
poolResponse.setCapacityIops(pool.getCapacityIops());
235+
236+
if (storagePool != null) {
237+
poolResponse.setManaged(storagePool.isManaged());
238+
if (storagePool.isManaged()) {
239+
DataStore store = dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary);
240+
PrimaryDataStoreDriver driver = (PrimaryDataStoreDriver) store.getDriver();
241+
long usedIops = driver.getUsedIops(storagePool);
242+
poolResponse.setAllocatedIops(usedIops);
243+
}
244+
}
245+
233246
poolResponse.setOverProvisionFactor(Double.toString(CapacityManager.StorageOverprovisioningFactor.valueIn(pool.getId())));
234247

235248
// TODO: StatsCollector does not persist data

server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,8 @@
307307
import com.google.common.collect.Sets;
308308
import com.googlecode.ipv6.IPv6Network;
309309

310+
import static com.cloud.configuration.Config.SecStorageAllowedInternalDownloadSites;
311+
310312
public class ConfigurationManagerImpl extends ManagerBase implements ConfigurationManager, ConfigurationService, Configurable {
311313
public static final String PERACCOUNT = "peraccount";
312314
public static final String PERZONE = "perzone";
@@ -1320,6 +1322,18 @@ protected String validateConfigurationValue(final String name, String value, fin
13201322
}
13211323
}
13221324

1325+
if (type.equals(String.class)) {
1326+
if (name.equalsIgnoreCase(SecStorageAllowedInternalDownloadSites.key()) && StringUtils.isNotEmpty(value)) {
1327+
final String[] cidrs = value.split(",");
1328+
for (final String cidr : cidrs) {
1329+
if (!NetUtils.isValidIp4(cidr) && !NetUtils.isValidIp6(cidr) && !NetUtils.getCleanIp4Cidr(cidr).equals(cidr)) {
1330+
logger.error(String.format("Invalid CIDR %s value specified for the config %s", cidr, name));
1331+
throw new InvalidParameterValueException(String.format("Invalid CIDR %s value specified for the config %s", cidr, name));
1332+
}
1333+
}
1334+
}
1335+
}
1336+
13231337
if (configuration == null ) {
13241338
//range validation has to be done per case basis, for now
13251339
//return in case of Configkey parameters

server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4296,7 +4296,11 @@ private boolean needMoveVolume(VolumeVO existingVolume, VolumeInfo newVolume) {
42964296
}
42974297
} else if (storeForNewStoreScope.getScopeType() == ScopeType.HOST
42984298
&& (storeForExistingStoreScope.getScopeType() == ScopeType.CLUSTER || storeForExistingStoreScope.getScopeType() == ScopeType.ZONE)) {
4299-
Long hostId = _vmInstanceDao.findById(existingVolume.getInstanceId()).getHostId();
4299+
VMInstanceVO vm = _vmInstanceDao.findById(existingVolume.getInstanceId());
4300+
Long hostId = vm.getHostId();
4301+
if (hostId == null) {
4302+
hostId = vm.getLastHostId();
4303+
}
43004304
if (storeForNewStoreScope.getScopeId().equals(hostId)) {
43014305
return false;
43024306
}

server/src/main/java/org/apache/cloudstack/snapshot/SnapshotHelper.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,15 +104,21 @@ public void expungeTemporarySnapshot(boolean kvmSnapshotOnlyInPrimaryStorage, Sn
104104
return;
105105
}
106106

107-
logger.debug(String.format("Expunging snapshot [%s] due to it is a temporary backup to create a volume from snapshot. It is occurring because the global setting [%s]"
108-
+ " has the value [%s].", snapInfo.getId(), SnapshotInfo.BackupSnapshotAfterTakingSnapshot.key(), backupSnapshotAfterTakingSnapshot));
109-
110-
try {
111-
snapshotService.deleteSnapshot(snapInfo);
112-
} catch (CloudRuntimeException ex) {
113-
logger.warn(String.format("Unable to delete the temporary snapshot [%s] on secondary storage due to [%s]. We still will expunge the database reference, consider"
114-
+ " manually deleting the file [%s].", snapInfo.getId(), ex.getMessage(), snapInfo.getPath()), ex);
107+
if (!DataStoreRole.Image.equals(snapInfo.getDataStore().getRole())) {
108+
logger.debug(String.format("Expunge template for Snapshot [%s] is called for primary storage role. Not expunging it, " +
109+
"but we will still expunge the database reference of the snapshot for image storage role if any", snapInfo.getId()));
110+
} else {
111+
logger.debug(String.format("Expunging snapshot [%s] due to it is a temporary backup to create a volume from snapshot. It is occurring because the global setting [%s]"
112+
+ " has the value [%s].", snapInfo.getId(), SnapshotInfo.BackupSnapshotAfterTakingSnapshot.key(), backupSnapshotAfterTakingSnapshot));
113+
114+
try {
115+
snapshotService.deleteSnapshot(snapInfo);
116+
} catch (CloudRuntimeException ex) {
117+
logger.warn(String.format("Unable to delete the temporary snapshot [%s] on secondary storage due to [%s]. We still will expunge the database reference, consider"
118+
+ " manually deleting the file [%s].", snapInfo.getId(), ex.getMessage(), snapInfo.getPath()), ex);
119+
}
115120
}
121+
116122
long storeId = snapInfo.getDataStore().getId();
117123
if (!DataStoreRole.Image.equals(snapInfo.getDataStore().getRole())) {
118124
long zoneId = dataStorageManager.getStoreZoneId(storeId, snapInfo.getDataStore().getRole());

services/secondary-storage/controller/src/main/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@
157157
import com.cloud.vm.dao.UserVmDetailsDao;
158158
import com.cloud.vm.dao.VMInstanceDao;
159159

160+
import static com.cloud.configuration.Config.SecStorageAllowedInternalDownloadSites;
161+
160162
/**
161163
* Class to manage secondary storages. <br><br>
162164
* Possible secondary storage VM state transition cases:<br>
@@ -399,6 +401,9 @@ private List<String> getAllowedInternalSiteCidrs() {
399401
String[] cidrs = _allowedInternalSites.split(",");
400402
for (String cidr : cidrs) {
401403
if (NetUtils.isValidIp4Cidr(cidr) || NetUtils.isValidIp4(cidr) || !cidr.startsWith("0.0.0.0")) {
404+
if (NetUtils.getCleanIp4Cidr(cidr).equals(cidr)) {
405+
logger.warn(String.format("Invalid CIDR %s in %s", cidr, SecStorageAllowedInternalDownloadSites.key()));
406+
}
402407
allowedCidrs.add(cidr);
403408
}
404409
}

0 commit comments

Comments
 (0)