Compare commits

...

7 Commits

Author SHA1 Message Date
dahn f8fb155108
Merge 8a35487389 into bce3e54a7e 2026-01-22 15:03:18 +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
Daan Hoogland 8a35487389 refactor null checks 2025-12-29 19:11:06 +01:00
Daan Hoogland d681dfae78 constrained offerings should not have cpu speed of 0 2025-12-23 14:18:12 +01:00
10 changed files with 112 additions and 59 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

@ -3198,14 +3198,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
final String offeringName = cmd.getServiceOfferingName();
final String name = cmd.getServiceOfferingName();
if (name == null || name.length() == 0) {
throw new InvalidParameterValueException("Failed to create service offering: specify the name that has non-zero length");
}
final String displayText = cmd.getDisplayText();
if (displayText == null || displayText.length() == 0) {
throw new InvalidParameterValueException("Failed to create service offering " + name + ": specify the display text that has non-zero length");
}
checkNameAndText(name, displayText);
final Integer cpuNumber = cmd.getCpuNumber();
final Integer cpuSpeed = cmd.getCpuSpeed();
@ -3218,49 +3212,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
Integer minMemory = cmd.getMinMemory();
// Check if service offering is Custom,
// If Customized, the following conditions must hold
// 1. cpuNumber, cpuSpeed and memory should be all null
// 2. minCPU, maxCPU, minMemory and maxMemory should all be null or all specified
boolean isCustomized = cmd.isCustomized();
if (isCustomized) {
// validate specs
//restricting the createserviceoffering to allow setting all or none of the dynamic parameters to null
if (cpuNumber != null || memory != null) {
throw new InvalidParameterValueException("For creating a custom compute offering cpu and memory all should be null");
}
// if any of them is null, then all of them shoull be null
if (maxCPU == null || minCPU == null || maxMemory == null || minMemory == null || cpuSpeed == null) {
if (maxCPU != null || minCPU != null || maxMemory != null || minMemory != null || cpuSpeed != null) {
throw new InvalidParameterValueException("For creating a custom compute offering min/max cpu and min/max memory/cpu speed should all be null or all specified");
}
} else {
if (cpuSpeed.intValue() < 0 || cpuSpeed.longValue() > Integer.MAX_VALUE) {
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the cpu speed value between 1 and " + Integer.MAX_VALUE);
}
if ((maxCPU <= 0 || maxCPU.longValue() > Integer.MAX_VALUE) || (minCPU <= 0 || minCPU.longValue() > Integer.MAX_VALUE ) ) {
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the minimum or minimum cpu number value between 1 and " + Integer.MAX_VALUE);
}
if (minMemory < 32 || (minMemory.longValue() > Integer.MAX_VALUE) || (maxMemory.longValue() > Integer.MAX_VALUE)) {
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the memory value between 32 and " + Integer.MAX_VALUE + " MB");
}
// Persist min/max CPU and Memory parameters in the service_offering_details table
details.put(ApiConstants.MIN_MEMORY, minMemory.toString());
details.put(ApiConstants.MAX_MEMORY, maxMemory.toString());
details.put(ApiConstants.MIN_CPU_NUMBER, minCPU.toString());
details.put(ApiConstants.MAX_CPU_NUMBER, maxCPU.toString());
}
checkSpeedOnCustomOffering(cpuNumber, memory, maxCPU, minCPU, maxMemory, minMemory, cpuSpeed, offeringName, details);
} else {
Integer maxCPUCores = VM_SERVICE_OFFERING_MAX_CPU_CORES.value() == 0 ? Integer.MAX_VALUE: VM_SERVICE_OFFERING_MAX_CPU_CORES.value();
Integer maxRAMSize = VM_SERVICE_OFFERING_MAX_RAM_SIZE.value() == 0 ? Integer.MAX_VALUE: VM_SERVICE_OFFERING_MAX_RAM_SIZE.value();
if (cpuNumber != null && (cpuNumber.intValue() <= 0 || cpuNumber.longValue() > maxCPUCores)) {
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the cpu number value between 1 and " + maxCPUCores);
}
if (cpuSpeed == null || (cpuSpeed.intValue() < 0 || cpuSpeed.longValue() > Integer.MAX_VALUE)) {
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the cpu speed value between 0 and " + Integer.MAX_VALUE);
}
if (memory != null && (memory.intValue() < 32 || memory.longValue() > maxRAMSize)) {
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the memory value between 32 and " + maxRAMSize + " MB");
}
checkSpeedOnConstrainedOffering(cpuSpeed, offeringName, maxCPU, minCPU, maxMemory, minMemory, cpuNumber, memory);
}
// check if valid domain
@ -3382,6 +3338,79 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
cmd.getDiskOfferingStrictness(), cmd.isCustomized(), cmd.getEncryptRoot(), cmd.isPurgeResources());
}
/**
* If Customized, the following conditions must hold
* 1. cpuNumber, cpuSpeed and memory should be all null
* 2. minCPU, maxCPU, minMemory and maxMemory should all be null or all specified
* @param cpuNumber
* @param memory
* @param maxCPU
* @param minCPU
* @param maxMemory
* @param minMemory
* @param cpuSpeed
* @param offeringName
*/
private static void checkSpeedOnCustomOffering(Integer cpuNumber, Integer memory, Integer maxCPU, Integer minCPU, Integer maxMemory, Integer minMemory, Integer cpuSpeed, String offeringName, Map<String, String> details) {
// validate specs
//restricting the createserviceoffering to allow setting all or none of the dynamic parameters to null
if (cpuNumber != null || memory != null) {
throw new InvalidParameterValueException("For creating a custom compute offering cpu and memory all should be null");
}
// if any of them is null, then all of them should be null
if (maxCPU == null || minCPU == null || maxMemory == null || minMemory == null || cpuSpeed == null) {
if (maxCPU != null || minCPU != null || maxMemory != null || minMemory != null || cpuSpeed != null) {
throw new InvalidParameterValueException("For creating a custom compute offering min/max cpu and min/max memory/cpu speed should all be null or all specified");
}
} else {
if (cpuSpeed.intValue() < 0 || cpuSpeed.longValue() > Integer.MAX_VALUE) {
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the cpu speed value between 1 and " + Integer.MAX_VALUE);
}
if ((maxCPU <= 0 || maxCPU.longValue() > Integer.MAX_VALUE) || (minCPU <= 0 || minCPU.longValue() > Integer.MAX_VALUE ) ) {
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the minimum or minimum cpu number value between 1 and " + Integer.MAX_VALUE);
}
if (minMemory < 32 || (minMemory.longValue() > Integer.MAX_VALUE) || (maxMemory.longValue() > Integer.MAX_VALUE)) {
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the memory value between 32 and " + Integer.MAX_VALUE + " MB");
}
// Persist min/max CPU and Memory parameters in the service_offering_details table
details.put(ApiConstants.MIN_MEMORY, minMemory.toString());
details.put(ApiConstants.MAX_MEMORY, maxMemory.toString());
details.put(ApiConstants.MIN_CPU_NUMBER, minCPU.toString());
details.put(ApiConstants.MAX_CPU_NUMBER, maxCPU.toString());
}
}
private static void checkNameAndText(String name, String displayText) {
if (name == null || name.length() == 0) {
throw new InvalidParameterValueException("Failed to create service offering: specify the name that has non-zero length");
}
if (displayText == null || displayText.length() == 0) {
throw new InvalidParameterValueException("Failed to create service offering " + name + ": specify the display text that has non-zero length");
}
}
private static void checkSpeedOnConstrainedOffering(Integer cpuSpeed, String offeringName, Integer maxCPU, Integer minCPU, Integer maxMemory, Integer minMemory, Integer cpuNumber, Integer memory) {
// Check for the combination of zero speed and custom or constrained offering
if (cpuSpeed != null && cpuSpeed.intValue() == 0) {
if (maxCPU != null || minCPU != null || maxMemory != null || minMemory != null) {
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": cpu speed cannot be zero for constrained offerings");
}
}
Integer maxCPUCores = VM_SERVICE_OFFERING_MAX_CPU_CORES.value() == 0 ? Integer.MAX_VALUE: VM_SERVICE_OFFERING_MAX_CPU_CORES.value();
Integer maxRAMSize = VM_SERVICE_OFFERING_MAX_RAM_SIZE.value() == 0 ? Integer.MAX_VALUE: VM_SERVICE_OFFERING_MAX_RAM_SIZE.value();
if (cpuNumber != null && (cpuNumber.intValue() <= 0 || cpuNumber.longValue() > maxCPUCores)) {
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the cpu number value between 1 and " + maxCPUCores);
}
if (cpuSpeed == null || (cpuSpeed.intValue() < 0 || cpuSpeed.longValue() > Integer.MAX_VALUE)) {
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the cpu speed value between 0 and " + Integer.MAX_VALUE);
}
if (memory != null && (memory.intValue() < 32 || memory.longValue() > maxRAMSize)) {
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the memory value between 32 and " + maxRAMSize + " MB");
}
}
protected ServiceOfferingVO createServiceOffering(final long userId, final boolean isSystem, final VirtualMachine.Type vmType,
final String name, final Integer cpu, final Integer ramSize, final Integer speed, final String displayText, final String provisioningType, final boolean localStorageRequired,
final boolean offerHA, final boolean limitResourceUse, final boolean volatileVm, String tags, final List<Long> domainIds, List<Long> zoneIds, final String hostTag,

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 () {