Skip to content

Commit 70af55e

Browse files
authored
UI support for extraconfig in deploy and update instance (#11719)
1 parent 30cb8c7 commit 70af55e

File tree

9 files changed

+62
-7
lines changed

9 files changed

+62
-7
lines changed

api/src/main/java/org/apache/cloudstack/api/ApiConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public class ApiConstants {
2626
public static final String ACTIVATION_RULE = "activationrule";
2727
public static final String ACTIVITY = "activity";
2828
public static final String ADAPTER_TYPE = "adaptertype";
29+
public static final String ADDITONAL_CONFIG_ENABLED = "additionalconfigenabled";
2930
public static final String ADDRESS = "address";
3031
public static final String ALGORITHM = "algorithm";
3132
public static final String ALIAS = "alias";

api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ public void execute() {
7373
response.setSharedFsVmMinCpuCount((Integer)capabilities.get(ApiConstants.SHAREDFSVM_MIN_CPU_COUNT));
7474
response.setSharedFsVmMinRamSize((Integer)capabilities.get(ApiConstants.SHAREDFSVM_MIN_RAM_SIZE));
7575
response.setDynamicScalingEnabled((Boolean) capabilities.get(ApiConstants.DYNAMIC_SCALING_ENABLED));
76+
response.setAdditionalConfigEnabled((Boolean) capabilities.get(ApiConstants.ADDITONAL_CONFIG_ENABLED));
7677
response.setObjectName("capability");
7778
response.setResponseName(getCommandName());
7879
this.setResponseObject(response);

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ public class CapabilitiesResponse extends BaseResponse {
140140
@Param(description = "true if dynamically scaling for instances is enabled", since = "4.21.0")
141141
private Boolean dynamicScalingEnabled;
142142

143+
@SerializedName(ApiConstants.ADDITONAL_CONFIG_ENABLED)
144+
@Param(description = "true if additional configurations or extraconfig can be passed to Instances", since = "4.20.2")
145+
private Boolean additionalConfigEnabled;
146+
143147
public void setSecurityGroupsEnabled(boolean securityGroupsEnabled) {
144148
this.securityGroupsEnabled = securityGroupsEnabled;
145149
}
@@ -255,4 +259,8 @@ public void setSharedFsVmMinRamSize(Integer sharedFsVmMinRamSize) {
255259
public void setDynamicScalingEnabled(Boolean dynamicScalingEnabled) {
256260
this.dynamicScalingEnabled = dynamicScalingEnabled;
257261
}
262+
263+
public void setAdditionalConfigEnabled(Boolean additionalConfigEnabled) {
264+
this.additionalConfigEnabled = additionalConfigEnabled;
265+
}
258266
}

server/src/main/java/com/cloud/server/ManagementServerImpl.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4535,6 +4535,8 @@ public Map<String, Object> listCapabilities(final ListCapabilitiesCmd cmd) {
45354535
}
45364536
capabilities.put(ApiConstants.SHAREDFSVM_MIN_CPU_COUNT, fsVmMinCpu);
45374537
capabilities.put(ApiConstants.SHAREDFSVM_MIN_RAM_SIZE, fsVmMinRam);
4538+
capabilities.put(ApiConstants.ADDITONAL_CONFIG_ENABLED, UserVmManager.EnableAdditionalVmConfig.valueIn(caller.getId()));
4539+
45384540

45394541
return capabilities;
45404542
}

server/src/main/java/com/cloud/vm/UserVmManager.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,15 @@ public interface UserVmManager extends UserVmService {
8383
"If set to true, tags specified in `resource.limit.host.tags` are also included in vm.strict.host.tags.",
8484
true);
8585

86+
ConfigKey<Boolean> EnableAdditionalVmConfig = new ConfigKey<>(
87+
"Advanced",
88+
Boolean.class,
89+
"enable.additional.vm.configuration",
90+
"false",
91+
"allow additional arbitrary configuration to vm",
92+
true,
93+
ConfigKey.Scope.Account);
94+
8695
static final int MAX_USER_DATA_LENGTH_BYTES = 2048;
8796

8897
public static final String CKS_NODE = "cksnode";

server/src/main/java/com/cloud/vm/UserVmManagerImpl.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -670,9 +670,6 @@ public void setKubernetesServiceHelpers(final List<KubernetesServiceHelper> kube
670670
private static final ConfigKey<Boolean> AllowDeployVmIfGivenHostFails = new ConfigKey<Boolean>("Advanced", Boolean.class, "allow.deploy.vm.if.deploy.on.given.host.fails", "false",
671671
"allow vm to deploy on different host if vm fails to deploy on the given host ", true);
672672

673-
private static final ConfigKey<Boolean> EnableAdditionalVmConfig = new ConfigKey<>("Advanced", Boolean.class,
674-
"enable.additional.vm.configuration", "false", "allow additional arbitrary configuration to vm", true, ConfigKey.Scope.Account);
675-
676673
private static final ConfigKey<String> KvmAdditionalConfigAllowList = new ConfigKey<>(String.class,
677674
"allow.additional.vm.configuration.list.kvm", "Advanced", "", "Comma separated list of allowed additional configuration options.", true, ConfigKey.Scope.Account, null, null, EnableAdditionalVmConfig.key(), null, null, ConfigKey.Kind.CSV, null);
678675

@@ -6280,7 +6277,7 @@ public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityE
62806277
protected void persistExtraConfigVmware(String decodedUrl, UserVm vm) {
62816278
boolean isValidConfig = isValidKeyValuePair(decodedUrl);
62826279
if (isValidConfig) {
6283-
String[] extraConfigs = decodedUrl.split("\\r?\\n");
6280+
String[] extraConfigs = decodedUrl.split("\\r?\\n+");
62846281
for (String cfg : extraConfigs) {
62856282
// Validate cfg against unsupported operations set by admin here
62866283
String[] allowedKeyList = VmwareAdditionalConfigAllowList.value().split(",");
@@ -6308,7 +6305,7 @@ protected void persistExtraConfigVmware(String decodedUrl, UserVm vm) {
63086305
protected void persistExtraConfigXenServer(String decodedUrl, UserVm vm) {
63096306
boolean isValidConfig = isValidKeyValuePair(decodedUrl);
63106307
if (isValidConfig) {
6311-
String[] extraConfigs = decodedUrl.split("\\r?\\n");
6308+
String[] extraConfigs = decodedUrl.split("\\r?\\n+");
63126309
int i = 1;
63136310
String extraConfigKey = ApiConstants.EXTRA_CONFIG + "-";
63146311
for (String cfg : extraConfigs) {
@@ -6388,8 +6385,8 @@ protected void persistExtraConfigKvm(String decodedUrl, UserVm vm) {
63886385
// validate config against denied cfg commands
63896386
validateKvmExtraConfig(decodedUrl, vm.getAccountId());
63906387
String[] extraConfigs = decodedUrl.split("\n\n");
6388+
int i = 1;
63916389
for (String cfg : extraConfigs) {
6392-
int i = 1;
63936390
String[] cfgParts = cfg.split("\n");
63946391
String extraConfigKey = ApiConstants.EXTRA_CONFIG;
63956392
String extraConfigValue;

ui/public/locales/en.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,8 @@
975975
"label.externalid": "External Id",
976976
"label.externalloadbalanceripaddress": "External load balancer IP address.",
977977
"label.extra": "Extra arguments",
978+
"label.extraconfig": "Additional Configuration",
979+
"label.extraconfig.tooltip": "Additional configuration parameters (extraconfig) to pass to the instance in plain text",
978980
"label.f5": "F5",
979981
"label.f5.ip.loadbalancer": "F5 BIG-IP load balancer.",
980982
"label.failed": "Failed",

ui/src/views/compute/DeployVM.vue

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,12 @@
724724
</div>
725725
</a-card>
726726
</a-form-item>
727+
<a-form-item v-if="extraConfigEnabled" name="extraconfig" ref="extraconfig">
728+
<template #label>
729+
<tooltip-label :title="$t('label.extraconfig')" :tooltip="$t('label.extraconfig.tooltip')"/>
730+
</template>
731+
<a-textarea v-model:value="form.extraconfig"/>
732+
</a-form-item>
727733
<a-form-item :label="$t('label.affinity.groups')">
728734
<affinity-group-selection
729735
:items="options.affinityGroups"
@@ -1418,6 +1424,9 @@ export default {
14181424
dynamicScalingVmConfigValue () {
14191425
return this.$store.getters.features.dynamicscalingenabled
14201426
},
1427+
extraConfigEnabled () {
1428+
return this.$store.getters.features.additionalconfigenabled
1429+
},
14211430
isCustomizedDiskIOPS () {
14221431
return this.diskSelected?.iscustomizediops || false
14231432
},
@@ -2054,6 +2063,9 @@ export default {
20542063
if (isUserdataAllowed && values.userdata && values.userdata.length > 0) {
20552064
deployVmData.userdata = this.$toBase64AndURIEncoded(values.userdata)
20562065
}
2066+
if (values.extraconfig && values.extraconfig.length > 0) {
2067+
deployVmData.extraconfig = encodeURIComponent(values.extraconfig)
2068+
}
20572069
// step 2: select template/iso
20582070
if (this.tabKey === 'templateid') {
20592071
deployVmData.templateid = values.templateid

ui/src/views/compute/EditVM.vue

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@
9191
<a-textarea v-model:value="form.userdata">
9292
</a-textarea>
9393
</a-form-item>
94+
<a-form-item v-if="extraConfigEnabled">
95+
<template #label>
96+
<tooltip-label :title="$t('label.extraconfig')" :tooltip="$t('label.extraconfig.tooltip')"/>
97+
</template>
98+
<a-textarea v-model:value="form.extraconfig"/>
99+
</a-form-item>
94100
<a-form-item ref="securitygroupids" name="securitygroupids" :label="$t('label.security.groups')" v-if="securityGroupsEnabled">
95101
<a-select
96102
mode="multiple"
@@ -167,6 +173,19 @@ export default {
167173
}
168174
}
169175
},
176+
computed: {
177+
extraConfigEnabled () {
178+
return this.$store.getters.features.additionalconfigenabled
179+
},
180+
combinedExtraConfig () {
181+
if (!this.extraConfigEnabled || !this.resource.details) return ''
182+
const configs = Object.keys(this.resource.details)
183+
.filter(key => key.startsWith('extraconfig-'))
184+
.map(key => this.resource.details[key] || '')
185+
.filter(val => val.trim())
186+
return configs.join('\n\n')
187+
}
188+
},
170189
beforeCreate () {
171190
this.apiParams = this.$getApiParams('updateVirtualMachine')
172191
},
@@ -185,7 +204,8 @@ export default {
185204
deleteprotection: this.resource.deleteprotection,
186205
group: this.resource.group,
187206
userdata: '',
188-
haenable: this.resource.haenable
207+
haenable: this.resource.haenable,
208+
extraconfig: this.combinedExtraConfig
189209
})
190210
this.rules = reactive({})
191211
},
@@ -342,6 +362,9 @@ export default {
342362
if (values.userdata && values.userdata.length > 0) {
343363
params.userdata = this.$toBase64AndURIEncoded(values.userdata)
344364
}
365+
if (values.extraconfig && values.extraconfig.length > 0) {
366+
params.extraconfig = encodeURIComponent(values.extraconfig)
367+
}
345368
this.loading = true
346369
347370
api('updateVirtualMachine', {}, 'POST', params).then(json => {

0 commit comments

Comments
 (0)