Guard OS type update for iso/template with existing vms (#11215)

* Guard OS type update for iso/template with existing vms

* fix identation

* rename vm -> instance

* force update iso/template as true by default via api

* add missing message.success.update.iso label
This commit is contained in:
Abhisar Sinha 2025-07-22 18:42:24 +05:30 committed by GitHub
parent 6ad9296412
commit e7172161dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 53 additions and 3 deletions

View File

@ -223,6 +223,7 @@ public class ApiConstants {
public static final String FORCED_DESTROY_LOCAL_STORAGE = "forcedestroylocalstorage";
public static final String FORCE_DELETE_HOST = "forcedeletehost";
public static final String FORCE_MS_TO_IMPORT_VM_FILES = "forcemstoimportvmfiles";
public static final String FORCE_UPDATE_OS_TYPE = "forceupdateostype";
public static final String FORMAT = "format";
public static final String FOR_VIRTUAL_NETWORK = "forvirtualnetwork";
public static final String FOR_SYSTEM_VMS = "forsystemvms";

View File

@ -51,6 +51,10 @@ public abstract class BaseUpdateTemplateOrIsoCmd extends BaseCmd {
description = "the ID of the OS type that best represents the OS of this image.")
private Long osTypeId;
@Parameter(name = ApiConstants.FORCE_UPDATE_OS_TYPE, type = CommandType.BOOLEAN, since = "4.21", description = "Force OS type update. Warning: Updating OS type will " +
"update the guest OS configuration for all the existing Instances deployed with this template/iso, which may affect their behavior.")
private Boolean forceUpdateOsType;
@Parameter(name = ApiConstants.FORMAT, type = CommandType.STRING, description = "the format for the image")
private String format;
@ -112,6 +116,10 @@ public abstract class BaseUpdateTemplateOrIsoCmd extends BaseCmd {
return osTypeId;
}
public Boolean getForceUpdateOsType() {
return forceUpdateOsType;
}
public Boolean getPasswordEnabled() {
return passwordEnabled;
}

View File

@ -2238,6 +2238,12 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
sc.addAnd("state", SearchCriteria.Op.NEQ, State.Expunging);
List<VMInstanceVO> vms = _vmInstanceDao.search(sc, null);
if (vms != null && !vms.isEmpty()) {
if (Boolean.FALSE.equals(cmd.getForceUpdateOsType())) {
String message = String.format("Updating OS type will update the guest OS configuration " +
"for all of the %d Instance(s) deployed with this Template/ISO, which may affect their behavior. " +
"To proceed, please set the 'forceupdateostype' parameter to true.", vms.size());
throw new InvalidParameterValueException(message);
}
for (VMInstanceVO vm: vms) {
vm.setGuestOSId(guestOSId);
_vmInstanceDao.update(vm.getId(), vm);

View File

@ -1039,6 +1039,7 @@
"label.forbidden": "Forbidden",
"label.forced": "Force",
"label.force.ms.to.import.vm.files": "Enable to force OVF Download via Management Server. Disable to use KVM Host ovftool (if installed)",
"label.force.update.os.type": "Force update OS type",
"label.force.stop": "Force stop",
"label.force.reboot": "Force reboot",
"label.forceencap": "Force UDP encapsulation of ESP packets",
@ -3693,6 +3694,7 @@
"message.success.update.ipaddress": "Successfully updated IP address",
"message.success.update.iprange": "Successfully updated IP range",
"message.success.update.ipv4.subnet": "Successfully updated IPv4 subnet",
"message.success.update.iso": "Successfully updated ISO",
"message.success.update.kubeversion": "Successfully updated Kubernetes supported version",
"message.success.update.network": "Successfully updated Network",
"message.success.update.template": "Successfully updated Template",

View File

@ -67,6 +67,13 @@
</a-select-option>
</a-select>
</a-form-item>
<a-form-item name="forceupdateostype" ref="forceupdateostype" v-if="hasOstypeidChanged()">
<template #label>
<tooltip-label :title="$t('label.force.update.os.type')" :tooltip="apiParams.forceupdateostype.description"/>
</template>
<a-switch v-model:checked="form.forceupdateostype" />
</a-form-item>
<a-form-item name="isdynamicallyscalable" ref="isdynamicallyscalable">
<template #label>
<tooltip-label :title="$t('label.isdynamicallyscalable')" :tooltip="apiParams.isdynamicallyscalable.description"/>
@ -172,7 +179,8 @@ export default {
userdataid: null,
userdatapolicy: null,
userdatapolicylist: {},
architectureTypes: {}
architectureTypes: {},
originalOstypeid: null
}
},
beforeCreate () {
@ -195,7 +203,7 @@ export default {
displaytext: [{ required: true, message: this.$t('message.error.required.input') }],
ostypeid: [{ required: true, message: this.$t('message.error.select') }]
})
const resourceFields = ['name', 'displaytext', 'passwordenabled', 'isdynamicallyscalable', 'ostypeid', 'userdataid', 'userdatapolicy']
const resourceFields = ['name', 'displaytext', 'passwordenabled', 'isdynamicallyscalable', 'ostypeid', 'forceupdateostype', 'userdataid', 'userdatapolicy']
for (var field of resourceFields) {
var fieldValue = this.resource[field]
@ -207,6 +215,10 @@ export default {
case 'userdatapolicy':
this.userdatapolicy = fieldValue
break
case 'ostypeid':
this.form[field] = fieldValue
this.originalOstypeid = fieldValue
break
default:
this.form[field] = fieldValue
break
@ -214,6 +226,9 @@ export default {
}
}
},
hasOstypeidChanged () {
return this.form.ostypeid !== this.originalOstypeid
},
fetchData () {
this.fetchOsTypes()
this.architectureTypes.opts = this.$fetchCpuArchitectureTypes()
@ -295,6 +310,7 @@ export default {
if (!this.isValidValueForKey(values, key)) continue
params[key] = values[key]
}
params.forceupdateostype = this.form.forceupdateostype || false
postAPI('updateIso', params).then(json => {
if (this.userdataid !== null) {
this.linkUserdataToTemplate(this.userdataid, json.updateisoresponse.iso.id, this.userdatapolicy)

View File

@ -103,6 +103,14 @@
</a-select>
</a-form-item>
</a-col>
<a-col :md="24" :lg="24" v-if="hasOstypeidChanged()">
<a-form-item name="forceupdateostype" ref="forceupdateostype">
<template #label>
<tooltip-label :title="$t('label.force.update.os.type')" :tooltip="apiParams.forceupdateostype.description"/>
</template>
<a-switch v-model:checked="form.forceupdateostype" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="12">
@ -251,7 +259,8 @@ export default {
userdataid: null,
userdatapolicy: null,
userdatapolicylist: {},
architectureTypes: {}
architectureTypes: {},
originalOstypeid: null
}
},
beforeCreate () {
@ -295,6 +304,10 @@ export default {
case 'userdatapolicy':
this.userdatapolicy = fieldValue
break
case 'ostypeid':
this.form[field] = fieldValue
this.originalOstypeid = fieldValue
break
default:
this.form[field] = fieldValue
break
@ -510,6 +523,7 @@ export default {
}
params[key] = values[key]
}
params.forceupdateostype = this.form.forceupdateostype || false
postAPI('updateTemplate', params).then(json => {
if (this.userdataid !== null) {
this.linkUserdataToTemplate(this.userdataid, json.updatetemplateresponse.template.id, this.userdatapolicy)
@ -546,6 +560,9 @@ export default {
}).finally(() => {
this.loading = false
})
},
hasOstypeidChanged () {
return this.form.ostypeid !== this.originalOstypeid
}
}
}