Compare commits

...

9 Commits

Author SHA1 Message Date
Abhishek Kumar d4c0ee4c7a
Merge 3e908f8a1b into bce3e54a7e 2026-01-22 15:20:27 +01:00
Daman Arora bce3e54a7e
improve error handling for template upload notifications (#12412)
Co-authored-by: Daman Arora <daman.arora@shapeblue.com>
2026-01-22 15:02:46 +01:00
Nicolas Vazquez 6a9835904c
Fix for zoneids parameters length on updateAPIs (#12440) 2026-01-22 14:57:46 +01:00
Nicolas Vazquez 6846619a6f
Fix update network offering domainids size limitation (#12431) 2026-01-22 14:32:46 +01:00
Vishesh d1eb2822d9
Remove redundant Exceptions from logs for vm schedules (#12428) 2026-01-22 14:29:35 +01:00
Abhishek Kumar 3e908f8a1b
Merge branch '4.20' into honor-datadiskcontroller-attachvol 2026-01-20 09:37:15 +05:30
Abhishek Kumar 5dec11fc0b
Apply suggestion from @shwstppr 2026-01-20 09:36:58 +05:30
Abhishek Kumar e8ab2aa0f8
Apply suggestions from code review 2026-01-20 09:36:17 +05:30
Abhishek Kumar 99ca62756c
kvm: honour data disk controller for vm during attach volume
Fixes #12090

Some years back #4569 allowed honouring dataDiskController for KVM VMs
during start but the same behviour is not seen while attaching a
volume to a running VM.
This PR first checks in the AttachCommand if a valid controller is
present. If a valid controller/bus type is not present than existing
logic is used.

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
2026-01-16 18:53:24 +05:30
12 changed files with 118 additions and 71 deletions

View File

@ -78,6 +78,7 @@ public class UpdateNetworkOfferingCmd extends BaseCmd {
@Parameter(name = ApiConstants.DOMAIN_ID,
type = CommandType.STRING,
length = 4096,
description = "The ID of the containing domain(s) as comma separated string, public for public offerings")
private String domainIds;

View File

@ -75,6 +75,7 @@ public class UpdateDiskOfferingCmd extends BaseCmd {
@Parameter(name = ApiConstants.ZONE_ID,
type = CommandType.STRING,
description = "The ID of the containing zone(s) as comma separated string, all for all zones offerings",
length = 4096,
since = "4.13")
private String zoneIds;

View File

@ -69,6 +69,7 @@ public class UpdateServiceOfferingCmd extends BaseCmd {
@Parameter(name = ApiConstants.ZONE_ID,
type = CommandType.STRING,
description = "The ID of the containing zone(s) as comma separated string, all for all zones offerings",
length = 4096,
since = "4.13")
private String zoneIds;

View File

@ -65,6 +65,7 @@ public class UpdateVPCOfferingCmd extends BaseAsyncCmd {
@Parameter(name = ApiConstants.ZONE_ID,
type = CommandType.STRING,
description = "The ID of the containing zone(s) as comma separated string, all for all zones offerings",
length = 4096,
since = "4.13")
private String zoneIds;

View File

@ -31,4 +31,6 @@ public interface VMScheduledJobDao extends GenericDao<VMScheduledJobVO, Long> {
int expungeJobsForSchedules(List<Long> scheduleId, Date dateAfter);
int expungeJobsBefore(Date currentTimestamp);
VMScheduledJobVO findByScheduleAndTimestamp(long scheduleId, Date scheduledTimestamp);
}

View File

@ -39,6 +39,8 @@ public class VMScheduledJobDaoImpl extends GenericDaoBase<VMScheduledJobVO, Long
private final SearchBuilder<VMScheduledJobVO> expungeJobForScheduleSearch;
private final SearchBuilder<VMScheduledJobVO> scheduleAndTimestampSearch;
static final String SCHEDULED_TIMESTAMP = "scheduled_timestamp";
static final String VM_SCHEDULE_ID = "vm_schedule_id";
@ -58,6 +60,11 @@ public class VMScheduledJobDaoImpl extends GenericDaoBase<VMScheduledJobVO, Long
expungeJobForScheduleSearch.and(VM_SCHEDULE_ID, expungeJobForScheduleSearch.entity().getVmScheduleId(), SearchCriteria.Op.IN);
expungeJobForScheduleSearch.and(SCHEDULED_TIMESTAMP, expungeJobForScheduleSearch.entity().getScheduledTime(), SearchCriteria.Op.GTEQ);
expungeJobForScheduleSearch.done();
scheduleAndTimestampSearch = createSearchBuilder();
scheduleAndTimestampSearch.and(VM_SCHEDULE_ID, scheduleAndTimestampSearch.entity().getVmScheduleId(), SearchCriteria.Op.EQ);
scheduleAndTimestampSearch.and(SCHEDULED_TIMESTAMP, scheduleAndTimestampSearch.entity().getScheduledTime(), SearchCriteria.Op.EQ);
scheduleAndTimestampSearch.done();
}
/**
@ -92,4 +99,12 @@ public class VMScheduledJobDaoImpl extends GenericDaoBase<VMScheduledJobVO, Long
sc.setParameters(SCHEDULED_TIMESTAMP, date);
return expunge(sc);
}
@Override
public VMScheduledJobVO findByScheduleAndTimestamp(long scheduleId, Date scheduledTimestamp) {
SearchCriteria<VMScheduledJobVO> sc = scheduleAndTimestampSearch.create();
sc.setParameters(VM_SCHEDULE_ID, scheduleId);
sc.setParameters(SCHEDULED_TIMESTAMP, scheduledTimestamp);
return findOneBy(sc);
}
}

View File

@ -4371,14 +4371,13 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
String dataDiskController = details.get(VmDetailConstants.DATA_DISK_CONTROLLER);
if (StringUtils.isNotBlank(dataDiskController)) {
LOGGER.debug("Passed custom disk controller for DATA disk " + dataDiskController);
for (DiskDef.DiskBus bus : DiskDef.DiskBus.values()) {
if (bus.toString().equalsIgnoreCase(dataDiskController)) {
LOGGER.debug("Found matching enum for disk controller for DATA disk " + dataDiskController);
LOGGER.debug("Passed custom disk controller for DATA disk {}", dataDiskController);
DiskDef.DiskBus bus = DiskDef.DiskBus.fromValue(dataDiskController);
if (bus != null) {
LOGGER.debug("Found matching enum for disk controller for DATA disk {}", dataDiskController);
return bus;
}
}
}
return null;
}

View File

@ -686,6 +686,15 @@ public class LibvirtVMDef {
_bus = bus;
}
public static DiskBus fromValue(String bus) {
for (DiskBus b : DiskBus.values()) {
if (b.toString().equalsIgnoreCase(bus)) {
return b;
}
}
return null;
}
@Override
public String toString() {
return _bus;

View File

@ -1340,6 +1340,7 @@ public class KVMStorageProcessor implements StorageProcessor {
* @param iopsWriteRateMaxLength iops write rate max length
* @param cacheMode cache mode
* @param encryptDetails encrypt details
* @param controllerInfo
* @throws LibvirtException
* @throws InternalErrorException
*/
@ -1347,11 +1348,11 @@ public class KVMStorageProcessor implements StorageProcessor {
final String serial, final Long bytesReadRate, final Long bytesReadRateMax, final Long bytesReadRateMaxLength,
final Long bytesWriteRate, final Long bytesWriteRateMax, final Long bytesWriteRateMaxLength, final Long iopsReadRate,
final Long iopsReadRateMax, final Long iopsReadRateMaxLength, final Long iopsWriteRate, final Long iopsWriteRateMax,
final Long iopsWriteRateMaxLength, final String cacheMode, final DiskDef.LibvirtDiskEncryptDetails encryptDetails, Map<String, String> details)
final Long iopsWriteRateMaxLength, final String cacheMode, final DiskDef.LibvirtDiskEncryptDetails encryptDetails, Map<String, String> details, Map<String, String> controllerInfo)
throws LibvirtException, InternalErrorException {
attachOrDetachDisk(conn, attach, vmName, attachingDisk, devId, serial, bytesReadRate, bytesReadRateMax, bytesReadRateMaxLength,
bytesWriteRate, bytesWriteRateMax, bytesWriteRateMaxLength, iopsReadRate, iopsReadRateMax, iopsReadRateMaxLength, iopsWriteRate,
iopsWriteRateMax, iopsWriteRateMaxLength, cacheMode, encryptDetails, 0l, details);
iopsWriteRateMax, iopsWriteRateMaxLength, cacheMode, encryptDetails, 0l, details, controllerInfo);
}
/**
@ -1378,6 +1379,7 @@ public class KVMStorageProcessor implements StorageProcessor {
* @param cacheMode cache mode
* @param encryptDetails encrypt details
* @param waitDetachDevice value set in milliseconds to wait before assuming device removal failed
* @param controllerInfo
* @throws LibvirtException
* @throws InternalErrorException
*/
@ -1386,7 +1388,7 @@ public class KVMStorageProcessor implements StorageProcessor {
final Long bytesWriteRate, final Long bytesWriteRateMax, final Long bytesWriteRateMaxLength, final Long iopsReadRate,
final Long iopsReadRateMax, final Long iopsReadRateMaxLength, final Long iopsWriteRate, final Long iopsWriteRateMax,
final Long iopsWriteRateMaxLength, final String cacheMode, final DiskDef.LibvirtDiskEncryptDetails encryptDetails,
long waitDetachDevice, Map<String, String> details)
long waitDetachDevice, Map<String, String> details, Map<String, String> controllerInfo)
throws LibvirtException, InternalErrorException {
List<DiskDef> disks = null;
@ -1423,17 +1425,7 @@ public class KVMStorageProcessor implements StorageProcessor {
return;
}
} else {
DiskDef.DiskBus busT = DiskDef.DiskBus.VIRTIO;
for (final DiskDef disk : disks) {
if (disk.getDeviceType() == DeviceType.DISK) {
if (disk.getBusType() == DiskDef.DiskBus.SCSI) {
busT = DiskDef.DiskBus.SCSI;
} else if (disk.getBusType() == DiskDef.DiskBus.VIRTIOBLK) {
busT = DiskDef.DiskBus.VIRTIOBLK;
}
break;
}
}
DiskDef.DiskBus busT = getAttachDiskBusType(devId, disks, controllerInfo);
diskdef = new DiskDef();
if (busT == DiskDef.DiskBus.SCSI || busT == DiskDef.DiskBus.VIRTIOBLK) {
diskdef.setQemuDriver(true);
@ -1538,6 +1530,28 @@ public class KVMStorageProcessor implements StorageProcessor {
}
}
protected DiskDef.DiskBus getAttachDiskBusType(int deviceId, List<DiskDef> disks, Map<String, String> controllerInfo) {
String controllerKey = deviceId == 0 ? VmDetailConstants.ROOT_DISK_CONTROLLER : VmDetailConstants.DATA_DISK_CONTROLLER;
String diskController = MapUtils.getString(controllerInfo, controllerKey);
DiskDef.DiskBus busType = DiskDef.DiskBus.fromValue(diskController);
if (diskController != null) {
logger.debug("Using controller '{}' from command specified as {} while attaching disk (deviceId={})",
diskController, controllerKey, deviceId);
return busType;
}
for (final DiskDef disk : disks) {
if (disk.getDeviceType() != DeviceType.DISK) {
continue;
}
if (disk.getBusType() == DiskDef.DiskBus.SCSI) {
return DiskDef.DiskBus.SCSI;
} else if (disk.getBusType() == DiskDef.DiskBus.VIRTIOBLK) {
return DiskDef.DiskBus.VIRTIOBLK;
}
}
return DiskDef.DiskBus.VIRTIO;
}
@Override
public Answer attachVolume(final AttachCommand cmd) {
final DiskTO disk = cmd.getDisk();
@ -1565,7 +1579,8 @@ public class KVMStorageProcessor implements StorageProcessor {
vol.getBytesReadRate(), vol.getBytesReadRateMax(), vol.getBytesReadRateMaxLength(),
vol.getBytesWriteRate(), vol.getBytesWriteRateMax(), vol.getBytesWriteRateMaxLength(),
vol.getIopsReadRate(), vol.getIopsReadRateMax(), vol.getIopsReadRateMaxLength(),
vol.getIopsWriteRate(), vol.getIopsWriteRateMax(), vol.getIopsWriteRateMaxLength(), volCacheMode, encryptDetails, disk.getDetails());
vol.getIopsWriteRate(), vol.getIopsWriteRateMax(), vol.getIopsWriteRateMaxLength(), volCacheMode,
encryptDetails, disk.getDetails(), cmd.getControllerInfo());
return new AttachAnswer(disk);
} catch (final LibvirtException e) {
@ -1602,7 +1617,7 @@ public class KVMStorageProcessor implements StorageProcessor {
vol.getBytesReadRate(), vol.getBytesReadRateMax(), vol.getBytesReadRateMaxLength(),
vol.getBytesWriteRate(), vol.getBytesWriteRateMax(), vol.getBytesWriteRateMaxLength(),
vol.getIopsReadRate(), vol.getIopsReadRateMax(), vol.getIopsReadRateMaxLength(),
vol.getIopsWriteRate(), vol.getIopsWriteRateMax(), vol.getIopsWriteRateMaxLength(), volCacheMode, null, waitDetachDevice, null);
vol.getIopsWriteRate(), vol.getIopsWriteRateMax(), vol.getIopsWriteRateMaxLength(), volCacheMode, null, waitDetachDevice, null, null);
storagePoolMgr.disconnectPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), vol.getPath());

View File

@ -162,7 +162,13 @@ public class VMSchedulerImpl extends ManagerBase implements VMScheduler, Configu
}
Date scheduledDateTime = Date.from(ts.toInstant());
VMScheduledJobVO scheduledJob = new VMScheduledJobVO(vmSchedule.getVmId(), vmSchedule.getId(), vmSchedule.getAction(), scheduledDateTime);
VMScheduledJobVO scheduledJob = vmScheduledJobDao.findByScheduleAndTimestamp(vmSchedule.getId(), scheduledDateTime);
if (scheduledJob != null) {
logger.trace("Job is already scheduled for schedule {} at {}", vmSchedule, scheduledDateTime);
return scheduledDateTime;
}
scheduledJob = new VMScheduledJobVO(vmSchedule.getVmId(), vmSchedule.getId(), vmSchedule.getAction(), scheduledDateTime);
try {
vmScheduledJobDao.persist(scheduledJob);
ActionEventUtils.onScheduledActionEvent(User.UID_SYSTEM, vm.getAccountId(), actionEventMap.get(vmSchedule.getAction()),

View File

@ -218,18 +218,19 @@ export const notifierPlugin = {
if (error.response.status) {
msg = `${i18n.global.t('message.request.failed')} (${error.response.status})`
}
if (error.message) {
desc = error.message
}
if (error.response.headers && 'x-description' in error.response.headers) {
if (error.response.headers?.['x-description']) {
desc = error.response.headers['x-description']
}
if (desc === '' && error.response.data) {
} else if (error.response.data) {
const responseKey = _.findKey(error.response.data, 'errortext')
if (responseKey) {
desc = error.response.data[responseKey].errortext
} else if (typeof error.response.data === 'string') {
desc = error.response.data
}
}
if (!desc && error.message) {
desc = error.message
}
}
let countNotify = store.getters.countNotify
countNotify++

View File

@ -638,11 +638,7 @@ export default {
this.$emit('refresh-data')
this.closeAction()
}).catch(e => {
this.$notification.error({
message: this.$t('message.upload.failed'),
description: `${this.$t('message.upload.template.failed.description')} - ${e}`,
duration: 0
})
this.$notifyError(e)
})
},
fetchCustomHypervisorName () {