Skip to content

Commit 11538df

Browse files
Merge branch '4.22'
2 parents c361409 + 2a60305 commit 11538df

File tree

26 files changed

+600
-166
lines changed

26 files changed

+600
-166
lines changed

api/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public interface VolumeImportUnmanageService extends PluggableService, Configura
3737
Arrays.asList(Hypervisor.HypervisorType.KVM, Hypervisor.HypervisorType.VMware);
3838

3939
List<Storage.StoragePoolType> SUPPORTED_STORAGE_POOL_TYPES_FOR_KVM = Arrays.asList(Storage.StoragePoolType.NetworkFilesystem,
40-
Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.RBD);
40+
Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.RBD, Storage.StoragePoolType.SharedMountPoint);
4141

4242
ConfigKey<Boolean> AllowImportVolumeWithBackingFile = new ConfigKey<>(Boolean.class,
4343
"allow.import.volume.with.backing.file",

engine/schema/src/main/java/com/cloud/host/dao/HostTagsDao.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,9 @@ public interface HostTagsDao extends GenericDao<HostTagVO, Long> {
4545
HostTagResponse newHostTagResponse(HostTagVO hostTag);
4646

4747
List<HostTagVO> searchByIds(Long... hostTagIds);
48+
49+
/**
50+
* List all host tags defined on hosts within a cluster
51+
*/
52+
List<String> listByClusterId(Long clusterId);
4853
}

engine/schema/src/main/java/com/cloud/host/dao/HostTagsDaoImpl.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.apache.cloudstack.framework.config.ConfigKey;
2424
import org.apache.cloudstack.framework.config.Configurable;
2525
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
26+
import org.apache.commons.collections4.CollectionUtils;
2627
import org.apache.commons.lang3.StringUtils;
2728
import org.springframework.stereotype.Component;
2829

@@ -43,9 +44,12 @@ public class HostTagsDaoImpl extends GenericDaoBase<HostTagVO, Long> implements
4344
private final SearchBuilder<HostTagVO> stSearch;
4445
private final SearchBuilder<HostTagVO> tagIdsearch;
4546
private final SearchBuilder<HostTagVO> ImplicitTagsSearch;
47+
private final GenericSearchBuilder<HostTagVO, String> tagSearch;
4648

4749
@Inject
4850
private ConfigurationDao _configDao;
51+
@Inject
52+
private HostDao hostDao;
4953

5054
public HostTagsDaoImpl() {
5155
HostSearch = createSearchBuilder();
@@ -72,6 +76,11 @@ public HostTagsDaoImpl() {
7276
ImplicitTagsSearch.and("hostId", ImplicitTagsSearch.entity().getHostId(), SearchCriteria.Op.EQ);
7377
ImplicitTagsSearch.and("isImplicit", ImplicitTagsSearch.entity().getIsImplicit(), SearchCriteria.Op.EQ);
7478
ImplicitTagsSearch.done();
79+
80+
tagSearch = createSearchBuilder(String.class);
81+
tagSearch.selectFields(tagSearch.entity().getTag());
82+
tagSearch.and("hostIdIN", tagSearch.entity().getHostId(), SearchCriteria.Op.IN);
83+
tagSearch.done();
7584
}
7685

7786
@Override
@@ -235,4 +244,15 @@ public List<HostTagVO> searchByIds(Long... tagIds) {
235244

236245
return tagList;
237246
}
247+
248+
@Override
249+
public List<String> listByClusterId(Long clusterId) {
250+
List<Long> hostIds = hostDao.listIdsByClusterId(clusterId);
251+
if (CollectionUtils.isEmpty(hostIds)) {
252+
return new ArrayList<>();
253+
}
254+
SearchCriteria<String> sc = tagSearch.create();
255+
sc.setParameters("hostIdIN", hostIds.toArray());
256+
return customSearch(sc, null);
257+
}
238258
}

engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDaoImpl.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ protected void init() {
170170
CountSnapshotsByAccount.select(null, Func.COUNT, null);
171171
CountSnapshotsByAccount.and("account", CountSnapshotsByAccount.entity().getAccountId(), SearchCriteria.Op.EQ);
172172
CountSnapshotsByAccount.and("status", CountSnapshotsByAccount.entity().getState(), SearchCriteria.Op.NIN);
173+
CountSnapshotsByAccount.and("snapshotTypeNEQ", CountSnapshotsByAccount.entity().getSnapshotType(), SearchCriteria.Op.NIN);
173174
CountSnapshotsByAccount.and("removed", CountSnapshotsByAccount.entity().getRemoved(), SearchCriteria.Op.NULL);
174175
CountSnapshotsByAccount.done();
175176

@@ -220,6 +221,7 @@ public Long countSnapshotsForAccount(long accountId) {
220221
SearchCriteria<Long> sc = CountSnapshotsByAccount.create();
221222
sc.setParameters("account", accountId);
222223
sc.setParameters("status", State.Error, State.Destroyed);
224+
sc.setParameters("snapshotTypeNEQ", Snapshot.Type.GROUP.ordinal());
223225
return customSearch(sc, null).get(0);
224226
}
225227

engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ public interface SnapshotDataStoreDao extends GenericDao<SnapshotDataStoreVO, Lo
5151

5252
SnapshotDataStoreVO findBySnapshotIdAndDataStoreRoleAndState(long snapshotId, DataStoreRole role, ObjectInDataStoreStateMachine.State state);
5353

54+
List<SnapshotDataStoreVO> listBySnapshotIdAndDataStoreRoleAndStateIn(long snapshotId, DataStoreRole role, ObjectInDataStoreStateMachine.State... state);
55+
5456
List<SnapshotDataStoreVO> listReadyByVolumeIdAndCheckpointPathNotNull(long volumeId);
5557

5658
SnapshotDataStoreVO findOneBySnapshotId(long snapshotId, long zoneId);

engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDaoImpl.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
6868
protected SearchBuilder<SnapshotDataStoreVO> searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq;
6969
private SearchBuilder<SnapshotDataStoreVO> stateSearch;
7070
private SearchBuilder<SnapshotDataStoreVO> idStateNinSearch;
71+
private SearchBuilder<SnapshotDataStoreVO> idEqRoleEqStateInSearch;
7172
protected SearchBuilder<SnapshotVO> snapshotVOSearch;
7273
private SearchBuilder<SnapshotDataStoreVO> snapshotCreatedSearch;
7374
private SearchBuilder<SnapshotDataStoreVO> dataStoreAndInstallPathSearch;
@@ -151,6 +152,11 @@ public boolean configure(String name, Map<String, Object> params) throws Configu
151152
idStateNinSearch.and(STATE, idStateNinSearch.entity().getState(), SearchCriteria.Op.NOTIN);
152153
idStateNinSearch.done();
153154

155+
idEqRoleEqStateInSearch = createSearchBuilder();
156+
idEqRoleEqStateInSearch.and(SNAPSHOT_ID, idEqRoleEqStateInSearch.entity().getSnapshotId(), SearchCriteria.Op.EQ);
157+
idEqRoleEqStateInSearch.and(STORE_ROLE, idEqRoleEqStateInSearch.entity().getRole(), SearchCriteria.Op.EQ);
158+
idEqRoleEqStateInSearch.and(STATE, idEqRoleEqStateInSearch.entity().getState(), SearchCriteria.Op.IN);
159+
154160
snapshotVOSearch = snapshotDao.createSearchBuilder();
155161
snapshotVOSearch.and(VOLUME_ID, snapshotVOSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
156162
snapshotVOSearch.done();
@@ -387,6 +393,15 @@ public SnapshotDataStoreVO findBySnapshotIdAndDataStoreRoleAndState(long snapsho
387393
return findOneBy(sc);
388394
}
389395

396+
@Override
397+
public List<SnapshotDataStoreVO> listBySnapshotIdAndDataStoreRoleAndStateIn(long snapshotId, DataStoreRole role, State... state) {
398+
SearchCriteria<SnapshotDataStoreVO> sc = idEqRoleEqStateInSearch.create();
399+
sc.setParameters(SNAPSHOT_ID, snapshotId);
400+
sc.setParameters(STORE_ROLE, role);
401+
sc.setParameters(STATE, (Object[])state);
402+
return listBy(sc);
403+
}
404+
390405
@Override
391406
public SnapshotDataStoreVO findOneBySnapshotId(long snapshotId, long zoneId) {
392407
try (TransactionLegacy transactionLegacy = TransactionLegacy.currentTxn()) {

engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -687,22 +687,23 @@ CREATE TABLE IF NOT EXISTS `cloud`.`backup_details` (
687687
UPDATE `cloud`.`backups` b
688688
INNER JOIN `cloud`.`vm_instance` vm ON b.vm_id = vm.id
689689
SET b.backed_volumes = (
690-
SELECT CONCAT("[",
691-
GROUP_CONCAT(
692-
CONCAT(
693-
"{\"uuid\":\"", v.uuid, "\",",
694-
"\"type\":\"", v.volume_type, "\",",
695-
"\"size\":", v.`size`, ",",
696-
"\"path\":\"", IFNULL(v.path, 'null'), "\",",
697-
"\"deviceId\":", IFNULL(v.device_id, 'null'), ",",
698-
"\"diskOfferingId\":\"", doff.uuid, "\",",
699-
"\"minIops\":", IFNULL(v.min_iops, 'null'), ",",
700-
"\"maxIops\":", IFNULL(v.max_iops, 'null'),
701-
"}"
702-
)
703-
SEPARATOR ","
690+
SELECT COALESCE(
691+
CAST(
692+
JSON_ARRAYAGG(
693+
JSON_OBJECT(
694+
'uuid', v.uuid,
695+
'type', v.volume_type,
696+
'size', v.size,
697+
'path', v.path,
698+
'deviceId', v.device_id,
699+
'diskOfferingId', doff.uuid,
700+
'minIops', v.min_iops,
701+
'maxIops', v.max_iops
702+
)
703+
) AS CHAR
704704
),
705-
"]")
705+
'[]'
706+
)
706707
FROM `cloud`.`volumes` v
707708
LEFT JOIN `cloud`.`disk_offering` doff ON v.disk_offering_id = doff.id
708709
WHERE v.instance_id = vm.id
@@ -711,22 +712,23 @@ SET b.backed_volumes = (
711712
-- Add diskOfferingId, deviceId, minIops and maxIops to backup_volumes in vm_instance table
712713
UPDATE `cloud`.`vm_instance` vm
713714
SET vm.backup_volumes = (
714-
SELECT CONCAT("[",
715-
GROUP_CONCAT(
716-
CONCAT(
717-
"{\"uuid\":\"", v.uuid, "\",",
718-
"\"type\":\"", v.volume_type, "\",",
719-
"\"size\":", v.`size`, ",",
720-
"\"path\":\"", IFNULL(v.path, 'null'), "\",",
721-
"\"deviceId\":", IFNULL(v.device_id, 'null'), ",",
722-
"\"diskOfferingId\":\"", doff.uuid, "\",",
723-
"\"minIops\":", IFNULL(v.min_iops, 'null'), ",",
724-
"\"maxIops\":", IFNULL(v.max_iops, 'null'),
725-
"}"
726-
)
727-
SEPARATOR ","
715+
SELECT COALESCE(
716+
CAST(
717+
JSON_ARRAYAGG(
718+
JSON_OBJECT(
719+
'uuid', v.uuid,
720+
'type', v.volume_type,
721+
'size', v.size,
722+
'path', v.path,
723+
'deviceId', v.device_id,
724+
'diskOfferingId', doff.uuid,
725+
'minIops', v.min_iops,
726+
'maxIops', v.max_iops
727+
)
728+
) AS CHAR
728729
),
729-
"]")
730+
'[]'
731+
)
730732
FROM `cloud`.`volumes` v
731733
LEFT JOIN `cloud`.`disk_offering` doff ON v.disk_offering_id = doff.id
732734
WHERE v.instance_id = vm.id

engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/DefaultSnapshotStrategy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public class DefaultSnapshotStrategy extends SnapshotStrategyBase {
120120
private final List<Snapshot.State> snapshotStatesAbleToDeleteSnapshot = Arrays.asList(Snapshot.State.Destroying, Snapshot.State.Destroyed, Snapshot.State.Error, Snapshot.State.Hidden);
121121

122122
public SnapshotDataStoreVO getSnapshotImageStoreRef(long snapshotId, long zoneId) {
123-
List<SnapshotDataStoreVO> snaps = snapshotStoreDao.listReadyBySnapshot(snapshotId, DataStoreRole.Image);
123+
List<SnapshotDataStoreVO> snaps = snapshotStoreDao.listBySnapshotIdAndDataStoreRoleAndStateIn(snapshotId, DataStoreRole.Image, State.Ready, State.Hidden);
124124
for (SnapshotDataStoreVO ref : snaps) {
125125
if (zoneId == dataStoreMgr.getStoreZoneId(ref.getDataStoreId(), ref.getRole())) {
126126
return ref;

engine/storage/snapshot/src/test/java/org/apache/cloudstack/storage/snapshot/DefaultSnapshotStrategyTest.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -257,11 +257,6 @@ public void verifyIfTheSnapshotIsBeingUsedByAnyVolumeTestDetailsIsNotEmptyThrowC
257257

258258
@Test
259259
public void testGetSnapshotImageStoreRefNull() {
260-
SnapshotDataStoreVO ref1 = Mockito.mock(SnapshotDataStoreVO.class);
261-
Mockito.when(ref1.getDataStoreId()).thenReturn(1L);
262-
Mockito.when(ref1.getRole()).thenReturn(DataStoreRole.Image);
263-
Mockito.when(snapshotDataStoreDao.listReadyBySnapshot(Mockito.anyLong(), Mockito.any(DataStoreRole.class))).thenReturn(List.of(ref1));
264-
Mockito.when(dataStoreManager.getStoreZoneId(1L, DataStoreRole.Image)).thenReturn(2L);
265260
Assert.assertNull(defaultSnapshotStrategySpy.getSnapshotImageStoreRef(1L, 1L));
266261
}
267262

@@ -270,7 +265,7 @@ public void testGetSnapshotImageStoreRefNotNull() {
270265
SnapshotDataStoreVO ref1 = Mockito.mock(SnapshotDataStoreVO.class);
271266
Mockito.when(ref1.getDataStoreId()).thenReturn(1L);
272267
Mockito.when(ref1.getRole()).thenReturn(DataStoreRole.Image);
273-
Mockito.when(snapshotDataStoreDao.listReadyBySnapshot(Mockito.anyLong(), Mockito.any(DataStoreRole.class))).thenReturn(List.of(ref1));
268+
Mockito.when(snapshotDataStoreDao.listBySnapshotIdAndDataStoreRoleAndStateIn(Mockito.anyLong(), Mockito.any(DataStoreRole.class), Mockito.any(), Mockito.any())).thenReturn(List.of(ref1));
274269
Mockito.when(dataStoreManager.getStoreZoneId(1L, DataStoreRole.Image)).thenReturn(1L);
275270
Assert.assertNotNull(defaultSnapshotStrategySpy.getSnapshotImageStoreRef(1L, 1L));
276271
}

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtGpuDef.java

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -79,28 +79,47 @@ private void generatePciXml(StringBuilder gpuBuilder) {
7979
gpuBuilder.append(" <driver name='vfio'/>\n");
8080
gpuBuilder.append(" <source>\n");
8181

82-
// Parse the bus address (e.g., 00:02.0) into domain, bus, slot, function
83-
String domain = "0x0000";
84-
String bus = "0x00";
85-
String slot = "0x00";
86-
String function = "0x0";
82+
// Parse the bus address into domain, bus, slot, function. Two input formats are accepted:
83+
// - "dddd:bb:ss.f" full PCI address with domain (e.g. 0000:00:02.0)
84+
// - "bb:ss.f" legacy short BDF; domain defaults to 0000
85+
// Each segment is parsed as a hex integer and formatted with fixed widths
86+
// (domain: 4 hex digits, bus/slot: 2 hex digits, function: 1 hex digit) to
87+
// produce canonical libvirt XML values regardless of input casing or padding.
88+
int domainVal = 0, busVal = 0, slotVal = 0, funcVal = 0;
8789

8890
if (busAddress != null && !busAddress.isEmpty()) {
8991
String[] parts = busAddress.split(":");
90-
if (parts.length > 1) {
91-
bus = "0x" + parts[0];
92-
String[] slotFunctionParts = parts[1].split("\\.");
93-
if (slotFunctionParts.length > 0) {
94-
slot = "0x" + slotFunctionParts[0];
95-
if (slotFunctionParts.length > 1) {
96-
function = "0x" + slotFunctionParts[1].trim();
97-
}
92+
try {
93+
String slotFunction;
94+
if (parts.length == 3) {
95+
domainVal = Integer.parseInt(parts[0], 16);
96+
busVal = Integer.parseInt(parts[1], 16);
97+
slotFunction = parts[2];
98+
} else if (parts.length == 2) {
99+
busVal = Integer.parseInt(parts[0], 16);
100+
slotFunction = parts[1];
101+
} else {
102+
throw new IllegalArgumentException("Invalid PCI bus address format: '" + busAddress + "'");
98103
}
104+
String[] sf = slotFunction.split("\\.");
105+
if (sf.length == 2) {
106+
slotVal = Integer.parseInt(sf[0], 16);
107+
funcVal = Integer.parseInt(sf[1].trim(), 16);
108+
} else {
109+
throw new IllegalArgumentException("Invalid PCI bus address format: '" + busAddress + "'");
110+
}
111+
} catch (NumberFormatException e) {
112+
throw new IllegalArgumentException("Invalid PCI bus address format: '" + busAddress + "'", e);
99113
}
100114
}
101115

116+
String domain = String.format("0x%04x", domainVal);
117+
String bus = String.format("0x%02x", busVal);
118+
String slot = String.format("0x%02x", slotVal);
119+
String function = String.format("0x%x", funcVal);
120+
102121
gpuBuilder.append(" <address domain='").append(domain).append("' bus='").append(bus).append("' slot='")
103-
.append(slot).append("' function='").append(function.trim()).append("'/>\n");
122+
.append(slot).append("' function='").append(function).append("'/>\n");
104123
gpuBuilder.append(" </source>\n");
105124
gpuBuilder.append("</hostdev>\n");
106125
}

0 commit comments

Comments
 (0)