Copilot commented on code in PR #14413:
URL: https://github.com/apache/cloudstack/pull/14413#discussion_r4518104904
##########
ui/src/views/compute/wizard/ComputeSelection.vue:
##########
@@ -57,23 +57,42 @@
</a-col>
<a-col :md="colContraned" :lg="colContraned" v-if="isCustomized">
<a-form-item
- :label="$t('label.memory.mb')"
+ :label="$t('label.memory')"
:validate-status="errors.memory.status"
:help="errors.memory.message">
<a-row :gutter="12">
- <a-col :md="10" :lg="10" v-show="isConstrained && maxMemory &&
!isNaN(maxMemory)">
+ <a-col :md="14" :lg="14" v-show="isConstrained && maxMemory &&
!isNaN(maxMemory) && maxMemory < 1000000000">
Review Comment:
The same long predicate is duplicated in multiple bindings, and the
`1000000000` threshold is a magic number. Consider extracting a computed
boolean (e.g., `showMemorySlider`) and a named constant (or computed) for the
threshold (with a short comment describing why the cutoff exists), then reuse
it in both places to reduce repetition and make future changes safer.
##########
ui/src/views/compute/wizard/ComputeSelection.vue:
##########
@@ -57,23 +57,42 @@
</a-col>
<a-col :md="colContraned" :lg="colContraned" v-if="isCustomized">
<a-form-item
- :label="$t('label.memory.mb')"
+ :label="$t('label.memory')"
:validate-status="errors.memory.status"
:help="errors.memory.message">
<a-row :gutter="12">
- <a-col :md="10" :lg="10" v-show="isConstrained && maxMemory &&
!isNaN(maxMemory)">
+ <a-col :md="14" :lg="14" v-show="isConstrained && maxMemory &&
!isNaN(maxMemory) && maxMemory < 1000000000">
<a-slider
- :min="minMemory"
- :max="maxMemory"
- v-model:value="memoryInputValue"
+ :min="minMemoryInUnit"
+ :max="maxMemoryInUnit"
+ :step="memoryStep"
+ v-model:value="memoryDisplayValue"
@change="($event) => updateComputeMemory($event)"
/>
</a-col>
- <a-col :md="4" :lg="4">
- <a-input-number
- v-model:value="memoryInputValue"
- @change="($event) => updateComputeMemory($event)"
- />
+ <a-col :md="isConstrained && maxMemory && !isNaN(maxMemory) &&
maxMemory < 1000000000 ? 10 : 24" :lg="isConstrained && maxMemory &&
!isNaN(maxMemory) && maxMemory < 1000000000 ? 10 : 24">
Review Comment:
The same long predicate is duplicated in multiple bindings, and the
`1000000000` threshold is a magic number. Consider extracting a computed
boolean (e.g., `showMemorySlider`) and a named constant (or computed) for the
threshold (with a short comment describing why the cutoff exists), then reuse
it in both places to reduce repetition and make future changes safer.
##########
ui/src/views/compute/wizard/ComputeSelection.vue:
##########
@@ -192,6 +215,47 @@ export default {
}
return 8
+ },
+ isGiBAllowed () {
+ if (this.preFillContent && this.preFillContent.memory &&
(this.preFillContent.memory % 256 !== 0)) {
+ return false
+ }
+ if (this.initialMemoryValue > 0 && (this.initialMemoryValue % 256 !==
0)) {
+ return false
+ }
+ if (!this.isConstrained) {
+ return true
+ }
+ const isMinValid = !this.minMemory || (this.minMemory >= 256 &&
this.minMemory % 256 === 0)
+ const isMaxValid = !this.maxMemory || this.maxMemory >=
Number.MAX_SAFE_INTEGER || (this.maxMemory >= 256 && this.maxMemory % 256 === 0)
+ return Boolean(isMinValid && isMaxValid)
+ },
+ minMemoryInUnit () {
+ if (!this.minMemory) return 0
+ if (this.memoryUnit === 'GiB') {
+ return this.minMemory / 1024
+ }
+ return this.minMemory
+ },
+ maxMemoryInUnit () {
+ if (!this.maxMemory || this.maxMemory >= Number.MAX_SAFE_INTEGER) return 0
+ if (this.memoryUnit === 'GiB') {
+ return this.maxMemory / 1024
+ }
+ return this.maxMemory
+ },
+ memoryStep () {
+ if (this.memoryUnit === 'GiB') {
+ return 0.25
+ }
+ return 1
+ },
Review Comment:
The implementation hard-codes GiB stepping to 0.25 (and validates multiples
of 0.25), but the PR description says GiB mode uses a 0.01 step (or smaller for
sub‑GiB) and rounds display to 2 decimals. Either update the PR description to
match the enforced 0.25 GiB increments, or update the code to implement the
described 0.01/dynamic step behavior (and align validation accordingly).
##########
ui/src/views/compute/wizard/ComputeSelection.vue:
##########
@@ -57,23 +57,42 @@
</a-col>
<a-col :md="colContraned" :lg="colContraned" v-if="isCustomized">
<a-form-item
- :label="$t('label.memory.mb')"
+ :label="$t('label.memory')"
:validate-status="errors.memory.status"
:help="errors.memory.message">
<a-row :gutter="12">
- <a-col :md="10" :lg="10" v-show="isConstrained && maxMemory &&
!isNaN(maxMemory)">
+ <a-col :md="14" :lg="14" v-show="isConstrained && maxMemory &&
!isNaN(maxMemory) && maxMemory < 1000000000">
<a-slider
- :min="minMemory"
- :max="maxMemory"
- v-model:value="memoryInputValue"
+ :min="minMemoryInUnit"
+ :max="maxMemoryInUnit"
+ :step="memoryStep"
+ v-model:value="memoryDisplayValue"
@change="($event) => updateComputeMemory($event)"
/>
</a-col>
- <a-col :md="4" :lg="4">
- <a-input-number
- v-model:value="memoryInputValue"
- @change="($event) => updateComputeMemory($event)"
- />
+ <a-col :md="isConstrained && maxMemory && !isNaN(maxMemory) &&
maxMemory < 1000000000 ? 10 : 24" :lg="isConstrained && maxMemory &&
!isNaN(maxMemory) && maxMemory < 1000000000 ? 10 : 24">
+ <a-input-group compact style="display: flex">
+ <a-tooltip :title="memoryTooltipText" placement="topLeft"
style="flex: 1; display: flex">
+ <a-input-number
+ style="width: 100%; min-width: 60px"
+ :min="isConstrained ? minMemoryInUnit : 0"
+ :max="isConstrained && maxMemoryInUnit > 0 ?
maxMemoryInUnit : undefined"
+ :step="memoryStep"
+ :precision="memoryUnit === 'GiB' ? 2 : 0"
+ v-model:value="memoryDisplayValue"
+ @change="($event) => updateComputeMemory($event)"
+ />
+ </a-tooltip>
+ <a-select
+ v-model:value="memoryUnit"
+ style="width: 75px"
+ @change="handleUnitChange">
+ <a-select-option value="GiB" :title="!isGiBAllowed ?
$t('message.memory.gib.not.supported') : undefined">
Review Comment:
When GiB isn’t allowed, the UI still allows selecting “GiB” and then reverts
with a warning in `handleUnitChange`. Consider disabling the GiB option (e.g.,
via a `:disabled="!isGiBAllowed"` binding) so users can’t select an invalid
unit in the first place; this avoids state churn, repeated `$nextTick` flips,
and warning noise.
##########
ui/src/views/compute/wizard/ComputeSelection.vue:
##########
@@ -224,40 +303,95 @@ export default {
if (this.preFillContent.cpuspeed) {
this.cpuSpeedInputValue = this.preFillContent.cpuspeed
}
- if (this.preFillContent.memory) {
- this.memoryInputValue = this.preFillContent.memory
- }
+
this.updateComputeCpuNumber(this.preFillContent.cpunumber ||
this.cpuNumberInputValue)
this.updateComputeCpuSpeed(this.preFillContent.cpuspeed ||
this.cpuSpeedInputValue)
- this.updateComputeMemory(this.preFillContent.memory ||
this.memoryInputValue)
+ // Emit exact prefilled MiB without any conversion on load
+ this.$emit('update-compute-memory', this.memoryInputDecorator,
this.memoryInputValue)
+ },
+ handleUnitChange (newUnit) {
+ if (newUnit === 'GiB') {
+ const currentMiB = this.memoryUnit === 'MiB' ? this.memoryDisplayValue
: this.memoryInputValue
+ if (!this.isGiBAllowed || (currentMiB > 0 && currentMiB % 256 !== 0)) {
+ this.memoryUnit = 'MiB'
+ this.$nextTick(() => {
+ this.memoryUnit = 'MiB'
+ })
+ message.warning(
+ this.$t('message.memory.gib.not.supported') ||
+ 'GiB unit is not available because memory limits or values must be
multiples of 256 MiB (0.25 GiB). Please use MiB.'
+ )
+ return
+ }
+ }
+
+ if (newUnit === 'MiB') {
+ this.memoryDisplayValue = (this.memoryInputValue !== undefined &&
this.memoryInputValue !== null)
+ ? this.memoryInputValue
+ : (this.memoryDisplayValue ? Math.round(this.memoryDisplayValue *
1024) : undefined)
+ } else {
+ this.memoryDisplayValue = (this.memoryInputValue !== undefined &&
this.memoryInputValue !== null)
+ ? this.memoryInputValue / 1024
+ : (this.memoryDisplayValue ? this.memoryDisplayValue : undefined)
+ }
+ if (this.isConstrained) {
+ if (this.memoryDisplayValue < this.minMemoryInUnit) {
+ this.memoryDisplayValue = this.minMemoryInUnit
+ } else if (this.maxMemoryInUnit > 0 && this.memoryDisplayValue >
this.maxMemoryInUnit) {
+ this.memoryDisplayValue = this.maxMemoryInUnit
+ }
+ }
+ this.updateComputeMemory(this.memoryDisplayValue)
},
updateComputeCpuNumber (value) {
- if (!value) this.cpuNumberInputValue = 0
+ this.cpuNumberInputValue = value
if (!this.validateInput('cpu', value)) {
+ this.$emit('update-compute-cpunumber', this.cpuNumberInputDecorator,
undefined)
return
}
this.$emit('update-compute-cpunumber', this.cpuNumberInputDecorator,
value)
},
updateComputeCpuSpeed (value) {
this.$emit('update-compute-cpuspeed', this.cpuSpeedInputDecorator, value)
},
- updateComputeMemory (value) {
- if (!value) this.memoryInputValue = 0
- if (!this.validateInput('memory', value)) {
+ updateComputeMemory (displayValue) {
+ this.memoryDisplayValue = displayValue
+ if (!this.validateInput('memory', displayValue)) {
+ this.memoryInputValue = undefined
+ this.$emit('update-compute-memory', this.memoryInputDecorator,
undefined)
return
}
- this.$emit('update-compute-memory', this.memoryInputDecorator, value)
+ let memoryInMiB
+ if (this.memoryUnit === 'GiB') {
+ memoryInMiB = Math.round(displayValue * 1024)
+ } else {
+ memoryInMiB = Math.round(displayValue)
+ }
+
Review Comment:
Using `Math.round(displayValue * 1024)` in GiB mode can yield a MiB value
that is slightly *less* than the user-entered GiB amount (e.g., 1.01 GiB →
1034.24 MiB → 1034 MiB). If the intent is “at least the requested GiB”, prefer
ceiling in GiB mode (while still clamping to max constraints). Also, the PR
description mentions “Exact Boundary Protection” for min/max constraints, but
this method doesn’t currently map values at the displayed min/max back to the
exact `minMemory`/`maxMemory` MiB values; consider explicitly snapping when
`displayValue` is within an epsilon of `minMemoryInUnit`/`maxMemoryInUnit`.
##########
ui/src/views/compute/wizard/ComputeSelection.vue:
##########
@@ -224,40 +303,95 @@ export default {
if (this.preFillContent.cpuspeed) {
this.cpuSpeedInputValue = this.preFillContent.cpuspeed
}
- if (this.preFillContent.memory) {
- this.memoryInputValue = this.preFillContent.memory
- }
+
this.updateComputeCpuNumber(this.preFillContent.cpunumber ||
this.cpuNumberInputValue)
this.updateComputeCpuSpeed(this.preFillContent.cpuspeed ||
this.cpuSpeedInputValue)
- this.updateComputeMemory(this.preFillContent.memory ||
this.memoryInputValue)
+ // Emit exact prefilled MiB without any conversion on load
+ this.$emit('update-compute-memory', this.memoryInputDecorator,
this.memoryInputValue)
+ },
+ handleUnitChange (newUnit) {
+ if (newUnit === 'GiB') {
+ const currentMiB = this.memoryUnit === 'MiB' ? this.memoryDisplayValue
: this.memoryInputValue
+ if (!this.isGiBAllowed || (currentMiB > 0 && currentMiB % 256 !== 0)) {
+ this.memoryUnit = 'MiB'
+ this.$nextTick(() => {
+ this.memoryUnit = 'MiB'
+ })
+ message.warning(
+ this.$t('message.memory.gib.not.supported') ||
+ 'GiB unit is not available because memory limits or values must be
multiples of 256 MiB (0.25 GiB). Please use MiB.'
+ )
+ return
+ }
+ }
+
+ if (newUnit === 'MiB') {
+ this.memoryDisplayValue = (this.memoryInputValue !== undefined &&
this.memoryInputValue !== null)
+ ? this.memoryInputValue
+ : (this.memoryDisplayValue ? Math.round(this.memoryDisplayValue *
1024) : undefined)
+ } else {
+ this.memoryDisplayValue = (this.memoryInputValue !== undefined &&
this.memoryInputValue !== null)
+ ? this.memoryInputValue / 1024
+ : (this.memoryDisplayValue ? this.memoryDisplayValue : undefined)
+ }
+ if (this.isConstrained) {
+ if (this.memoryDisplayValue < this.minMemoryInUnit) {
+ this.memoryDisplayValue = this.minMemoryInUnit
+ } else if (this.maxMemoryInUnit > 0 && this.memoryDisplayValue >
this.maxMemoryInUnit) {
+ this.memoryDisplayValue = this.maxMemoryInUnit
+ }
+ }
+ this.updateComputeMemory(this.memoryDisplayValue)
},
updateComputeCpuNumber (value) {
- if (!value) this.cpuNumberInputValue = 0
+ this.cpuNumberInputValue = value
if (!this.validateInput('cpu', value)) {
+ this.$emit('update-compute-cpunumber', this.cpuNumberInputDecorator,
undefined)
return
}
this.$emit('update-compute-cpunumber', this.cpuNumberInputDecorator,
value)
},
updateComputeCpuSpeed (value) {
this.$emit('update-compute-cpuspeed', this.cpuSpeedInputDecorator, value)
},
- updateComputeMemory (value) {
- if (!value) this.memoryInputValue = 0
- if (!this.validateInput('memory', value)) {
+ updateComputeMemory (displayValue) {
+ this.memoryDisplayValue = displayValue
+ if (!this.validateInput('memory', displayValue)) {
+ this.memoryInputValue = undefined
+ this.$emit('update-compute-memory', this.memoryInputDecorator,
undefined)
return
}
- this.$emit('update-compute-memory', this.memoryInputDecorator, value)
+ let memoryInMiB
+ if (this.memoryUnit === 'GiB') {
+ memoryInMiB = Math.round(displayValue * 1024)
+ } else {
+ memoryInMiB = Math.round(displayValue)
+ }
+
+ this.memoryInputValue = memoryInMiB
+ this.$emit('update-compute-memory', this.memoryInputDecorator,
memoryInMiB)
},
validateInput (input, value) {
this.errors[input].status = ''
this.errors[input].message = ''
- if (value === null || value === undefined || value.length === 0) {
+ if (value === null || value === undefined || value.length === 0 || value
=== '' || value === 0) {
this.errors[input].status = 'error'
this.errors[input].message = this.$t('message.error.required.input')
return false
}
+ if (input === 'memory') {
+ if (this.memoryUnit === 'GiB') {
+ const isStepMultiple = Math.abs(value * 4 - Math.round(value * 4)) <
0.001
+ if (!isStepMultiple) {
+ this.errors.memory.status = 'error'
+ this.errors.memory.message =
this.$t('message.error.memory.gib.step') || 'Please enter memory in multiples
of 0.25 GiB (256 MiB), or switch to MiB.'
+ return false
+ }
+ }
+ }
Review Comment:
The implementation hard-codes GiB stepping to 0.25 (and validates multiples
of 0.25), but the PR description says GiB mode uses a 0.01 step (or smaller for
sub‑GiB) and rounds display to 2 decimals. Either update the PR description to
match the enforced 0.25 GiB increments, or update the code to implement the
described 0.01/dynamic step behavior (and align validation accordingly).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]