Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ui/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3667,6 +3667,7 @@
"message.error.mtu.below.min": "MTU is below the minimum supported value of %x",
"message.error.mtu.public.max.exceed": "The value entered exceeds the maximum allowed public MTU for this Zone, your value will be automatically lowered to match it.",
"message.error.mtu.private.max.exceed": "The value entered exceeds the maximum allowed private MTU for this Zone, your value will be automatically lowered to match it.",
"message.error.memory.gib.step": "Please enter memory in multiples of 0.25 GiB (256 MiB), or switch to MiB.",
"message.error.name": "Please enter name.",
"message.error.netmask": "Please enter Netmask.",
"message.error.network.offering": "Please select Network offering.",
Expand Down Expand Up @@ -3861,6 +3862,7 @@
"message.lock.user.success": "Successfully locked User \"{user}\"",
"message.login.failed": "Login Failed",
"message.maintenance.initiated": "Maintenance has been initiated. This Management Server will not accept new jobs",
"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.",
"message.migrate.instance.host.auto.assign": "Host for the Instance will be automatically chosen based on the suitability within the same Cluster",
"message.migrate.instance.to.host": "Please confirm that you want to migrate this Instance to another host. When migration is between hosts of different Clusters volume(s) of the Instance may get migrated to suitable storage pools.",
"message.migrate.instance.to.ps": "Please confirm that you want to migrate this Instance to another primary storage.",
Expand Down
190 changes: 164 additions & 26 deletions ui/src/views/compute/wizard/ComputeSelection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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')"
Comment thread
saurabhkushwaha438 marked this conversation as resolved.
Resolved
: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">
GiB
</a-select-option>
<a-select-option value="MiB">MiB</a-select-option>
</a-select>
</a-input-group>
</a-col>
</a-row>
</a-form-item>
Expand All @@ -96,6 +115,8 @@
</template>

<script>
import { message } from 'ant-design-vue'

export default {
name: 'ComputeSelection',
props: {
Expand Down Expand Up @@ -165,6 +186,8 @@ export default {
cpuNumberInputValue: 0,
cpuSpeedInputValue: 0,
memoryInputValue: 0,
memoryDisplayValue: 0,
memoryUnit: 'GiB',
errors: {
cpu: {
status: '',
Expand Down Expand Up @@ -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
},
memoryTooltipText () {
if (!this.memoryInputValue || this.memoryInputValue <= 0) return ''
if (this.memoryUnit === 'GiB') {
return `${this.memoryDisplayValue} GiB = ${this.memoryInputValue} MiB`
}
return ''
}
},
watch: {
Expand All @@ -209,13 +273,28 @@ export default {
methods: {
fillValue () {
this.cpuNumberInputValue = this.initialCpuValue > 0 ? this.initialCpuValue : this.minCpu
this.memoryInputValue = this.initialMemoryValue > 0 ? this.initialMemoryValue : this.minMemory
const rawMemory = (this.preFillContent && this.preFillContent.memory)
? this.preFillContent.memory
: (this.initialMemoryValue > 0 ? this.initialMemoryValue : this.minMemory)

// Default to GiB only if GiB is allowed and rawMemory is 0 or multiple of 256 MiB
const shouldUseGiB = this.isGiBAllowed && (rawMemory === 0 || (rawMemory >= 256 && rawMemory % 256 === 0))
this.memoryUnit = shouldUseGiB ? 'GiB' : 'MiB'

if (rawMemory > 0) {
this.memoryInputValue = rawMemory
this.memoryDisplayValue = shouldUseGiB ? (rawMemory / 1024) : rawMemory
} else {
this.memoryInputValue = undefined
this.memoryDisplayValue = undefined
}

this.cpuSpeedInputValue = this.cpuSpeed

if (!this.preFillContent) {
this.updateComputeCpuNumber(this.cpuNumberInputValue)
this.updateComputeCpuSpeed(this.cpuSpeedInputValue)
this.updateComputeMemory(this.memoryInputValue)
this.$emit('update-compute-memory', this.memoryInputDecorator, this.memoryInputValue)
return
}
if (this.preFillContent.cpunumber) {
Expand All @@ -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') {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@saurabhkushwaha438 The memory field and its hover text show different amounts of memory value at the minimum and maximum of a constrained offering.
With min memory 100 MiB, the GiB control shows 0.25 GiB. Hovering it shows "Exact: 0.09765625 GiB (100 MiB)", and deploy sends 100 MiB. 0.25 GiB is 256 MiB, so the VM is created with a smaller size than the field shows. The same happens at the maximum.

Image Image

memoryInMiB = Math.round(displayValue * 1024)
} else {
memoryInMiB = Math.round(displayValue)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@saurabhkushwaha438 Switching between MiB and GiB updates only the displayed memory value, while the deploy form continues to retain the previously entered MiB value.

On an unconstrained custom offering:

  • Enter 1100 MiB and then switch to GiB. The field displays 1.00 GiB, but when the instance is created, the deploy request still sends 1100 MiB (where 1.00 GiB = 1024 MiB).
  • Enter 100 MiB and then switch to GiB. The field displays 0 GiB, but instance creation still succeeds, and the deploy request sends 100 MiB, resulting in a VM being created with 100 MiB of memory.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for pointing out this "Enter 100 MiB and then switch to GiB. The field displays 0 GiB, but instance creation still succeeds, and the deploy request sends 100 MiB, resulting in a VM being created with 100 MiB of memory."

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for pointing out this "Enter 100 MiB and then switch to GiB. The field displays 0 GiB, but instance creation still succeeds, and the deploy request sends 100 MiB, resulting in a VM being created with 100 MiB of memory."

hey i am not able reproduce this problem , could you attach screenshot of it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please try this with custom unconstrained compute offering?

}

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@saurabhkushwaha438 Setting the CPU value to 0 does not clear the previously stored CPU count in the form. For example, after selecting a constrained offering, the form stores CPU = 2. When switching to an unconstrained custom offering, the CPU field displays 0 with the validation message "Please enter input". However, instance creation still proceeds successfully, and the VM is created with 2 CPUs, as the previously stored value is retained.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well this is not related to this issue

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
}
}
}

if (!this.isConstrained) {
return true
}
Expand All @@ -271,21 +405,25 @@ export default {
max = this.maxCpu
break
case 'memory':
min = this.minMemory
max = this.maxMemory
min = this.minMemoryInUnit
max = this.maxMemoryInUnit
break
}

if (!this.checkValidRange(value, min, max)) {
this.errors[input].status = 'error'
this.errors[input].message = `${this.$t('message.please.enter.value')} ${this.$t('label.from')} ${min} ${this.$t('label.to')} ${max})`
this.errors[input].message = `${this.$t('message.please.enter.value')} ${this.$t('label.from')} ${min} ${this.$t('label.to')} ${max}`
return false
}

return true
},
checkValidRange (value, min, max) {
if (value < min || value > max) {
const epsilon = 0.001
if (min !== undefined && min !== null && value < (min - epsilon)) {
return false
}
if (max !== undefined && max !== null && max > 0 && max < Number.MAX_SAFE_INTEGER && value > (max + epsilon)) {
return false
}

Expand Down
Loading