This is an automated email from the ASF dual-hosted git repository.
DaanHoogland pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/main by this push:
new 5c97979d458 ui: show related settings when (main) config value is
changed (#13965)
5c97979d458 is described below
commit 5c97979d458f23f608f120011d727474a4f9abf6
Author: Abhishek Kumar <[email protected]>
AuthorDate: Tue Sep 8 17:35:54 2026 +0530
ui: show related settings when (main) config value is changed (#13965)
Signed-off-by: Abhishek Kumar <[email protected]>
---
ui/public/locales/en.json | 2 +
ui/src/views/setting/ConfigurationValue.vue | 122 +++++++++++++++++++++++++++-
2 files changed, 121 insertions(+), 3 deletions(-)
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index 83f7d182285..19034d4c4db 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -2203,6 +2203,7 @@
"label.reinstall.vm": "Reinstall Instance",
"label.reject": "Reject",
"label.related": "Related",
+"label.related.settings": "Related Settings",
"label.relationaloperator": "Operator",
"label.release": "Release",
"label.release.account": "Release from Account",
@@ -3938,6 +3939,7 @@
"message.read.admin.guide.scaling.up": "Please read the dynamic scaling
section in the admin guide before scaling up.",
"message.recover.vm": "Please confirm that you would like to recover this
Instance.",
"message.reinstall.vm": "NOTE: Proceed with caution. This will cause the
Instance to be reinstalled from the Template; data on the root disk will be
lost. Extra data volumes, if any, will not be touched.",
+"message.related.settings.changed": "'%x' has been changed. Would you like to
review or update any related settings below?",
"message.release.ip.failed": "Failed to release IP",
"message.releasing.dedicated.cluster": "Releasing dedicated Cluster...",
"message.releasing.dedicated.host": "Releasing dedicated host...",
diff --git a/ui/src/views/setting/ConfigurationValue.vue
b/ui/src/views/setting/ConfigurationValue.vue
index 0979c3c03cb..ea146df8cb3 100644
--- a/ui/src/views/setting/ConfigurationValue.vue
+++ b/ui/src/views/setting/ConfigurationValue.vue
@@ -190,10 +190,41 @@
:disabled="(!('resetConfiguration' in $store.getters.apis) ||
configDisabled || valueLoading || configrecord.value ===
configrecord.defaultvalue)" />
</span>
</a-list-item>
+ <a-modal
+ v-if="!suppressRelatedPrompt"
+ v-model:visible="relatedModalVisible"
+ :title="$t('label.related.settings')"
+ :footer="null"
+ :maskClosable="false"
+ :width="'60vw'"
+ @cancel="closeRelatedModal">
+ <p>{{ $t('message.related.settings.changed').replace('%x',
relatedSourceConfigName) }}</p>
+ <a-table
+ size="small"
+ :showHeader="false"
+ :pagination="false"
+ :loading="relatedLoading"
+ :columns="relatedColumns"
+ :dataSource="relatedConfigs"
+ :rowKey="record => record.name">
+ <template #bodyCell="{ column, record }">
+ <template v-if="column.key === 'name'">
+ <b>{{ record.displaytext }}</b> {{ ' (' + record.name + ')' }}
<br/> {{ record.description }}
+ </template>
+ <template v-if="column.key === 'value'">
+ <ConfigurationValue
+ :configrecord="record"
+ :resource="resource"
+ :suppressRelatedPrompt="true"
+ @refresh="handleRelatedConfigRefresh" />
+ </template>
+ </template>
+ </a-table>
+ </a-modal>
</a-list>
</template>
<script>
-import { postAPI } from '@/api'
+import { getAPI, postAPI } from '@/api'
import TooltipButton from '@/components/widgets/TooltipButton'
export default {
@@ -221,6 +252,10 @@ export default {
resource: {
type: Object,
required: false
+ },
+ suppressRelatedPrompt: {
+ type: Boolean,
+ default: false
}
},
data () {
@@ -229,7 +264,24 @@ export default {
scopeKey: '',
actualValue: null,
editableValue: null,
- editableValueKey: null
+ editableValueKey: null,
+ relatedModalVisible: false,
+ relatedLoading: false,
+ relatedConfigs: [],
+ relatedSourceConfigName: '',
+ relatedColumns: [
+ {
+ title: 'name',
+ dataIndex: 'name',
+ key: 'name'
+ },
+ {
+ title: 'value',
+ dataIndex: 'value',
+ key: 'value',
+ width: '35%'
+ }
+ ]
}
},
created () {
@@ -259,8 +311,12 @@ export default {
},
watch: {
configrecord: {
- handler () {
+ handler (newRecord) {
this.setConfigData()
+ if (newRecord && newRecord.type === 'Boolean' &&
!this.suppressRelatedPrompt &&
+ this.$route.meta.name === 'globalsetting' &&
this.isConfigNowActive(newRecord)) {
+ this.fetchRelatedConfigurations(newRecord)
+ }
},
deep: true
}
@@ -319,6 +375,66 @@ export default {
this.$emit('refresh', configrecord.name, configRecordEntry)
})
},
+ isConfigNowActive (configrecord) {
+ const name = configrecord.name || ''
+ if (name.endsWith('.disabled')) {
+ return configrecord.value === 'false'
+ }
+ return configrecord.value === 'true'
+ },
+ getRelatedConfigPrefix (configrecord) {
+ const name = configrecord.name || ''
+ const segments = name.split('.')
+ if (segments.length < 2) {
+ return null
+ }
+ let prefixSegments = segments.slice(0, -1)
+ if ((name.endsWith('.service.enabled') ||
name.endsWith('.service.disabled')) && prefixSegments.length > 1) {
+ prefixSegments = prefixSegments.slice(0, -1)
+ }
+ return prefixSegments.join('.')
+ },
+ fetchRelatedConfigurations (configrecord) {
+ const prefix = this.getRelatedConfigPrefix(configrecord)
+ if (!prefix) {
+ return
+ }
+ this.relatedLoading = true
+ const params = {
+ [this.scopeKey]: this.$route.params?.id,
+ keyword: prefix,
+ pagesize: -1,
+ listAll: true
+ }
+ if (this.scopeKey === 'domainid' && !params[this.scopeKey]) {
+ params[this.scopeKey] = this.resource?.id
+ }
+ getAPI('listConfigurations', params).then(json => {
+ const list = json?.listconfigurationsresponse?.configuration || []
+ this.relatedConfigs = list.filter(c => c.name !== configrecord.name &&
c.name.startsWith(prefix + '.'))
+ if (this.relatedConfigs.length > 0) {
+ this.relatedSourceConfigName = configrecord.name
+ this.relatedModalVisible = true
+ }
+ }).catch(error => {
+ console.error(error)
+ }).finally(() => {
+ this.relatedLoading = false
+ })
+ },
+ handleRelatedConfigRefresh (name, updatedRecord) {
+ if (!name || !updatedRecord) return
+ const index = this.relatedConfigs.findIndex(item => item.name === name)
+ if (index !== -1) {
+ this.relatedConfigs.splice(index, 1, updatedRecord)
+ }
+ this.$emit('refresh', name, updatedRecord)
+ },
+ closeRelatedModal () {
+ this.relatedModalVisible = false
+ this.relatedConfigs = []
+ this.relatedSourceConfigName = ''
+ },
resetConfigurationValue (configrecord) {
let configRecordEntry = this.configrecord
this.valueLoading = true