From ae3be5801ec4de6c41cd49f65a99caf41bac2f4d Mon Sep 17 00:00:00 2001
From: JS Choi <77760789+jschoiRR@users.noreply.github.com>
Date: Wed, 29 Apr 2026 12:51:35 +0900
Subject: [PATCH 1/3] =?UTF-8?q?VNF=20=EB=B0=B0=ED=8F=AC=20=EC=8B=9C=20temp?=
=?UTF-8?q?late=EC=9D=98=20vnfdetails=20=EA=B0=92=EC=9D=84=20userdatadetai?=
=?UTF-8?q?ls=EC=99=80=20password=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0?=
=?UTF-8?q?=EC=97=90=20=EB=B0=98=EC=98=81=ED=95=98=EB=8F=84=EB=A1=9D=20?=
=?UTF-8?q?=EB=B3=B4=EA=B0=95/=20=EB=B2=84=ED=82=B7=20=EC=83=81=EC=84=B8?=
=?UTF-8?q?=20=EC=99=BC=EC=AA=BD=20=EC=B9=B4=EB=93=9C=EC=97=90=20=EC=82=AC?=
=?UTF-8?q?=EC=9A=A9=EB=9F=89=20=EC=84=B9=EC=85=98=EC=9D=84=20=EC=B6=94?=
=?UTF-8?q?=EA=B0=80(=ED=8D=BC=EC=84=BC=ED=8A=B8=20=ED=91=9C=EC=8B=9C)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
ui/src/components/view/InfoCard.vue | 32 +++++++++++++
ui/src/views/compute/DeployVnfAppliance.vue | 52 ++++++++++++++++++---
2 files changed, 78 insertions(+), 6 deletions(-)
diff --git a/ui/src/components/view/InfoCard.vue b/ui/src/components/view/InfoCard.vue
index f351c33e619b..3ae0e8abfcfa 100644
--- a/ui/src/components/view/InfoCard.vue
+++ b/ui/src/components/view/InfoCard.vue
@@ -137,6 +137,21 @@
+
{{ $t('label.haenable') }}
@@ -1094,6 +1109,23 @@ export default {
}
return null
},
+ bucketQuotaKB () {
+ const quotaGiB = Number(this.resource?.quota)
+ if (!Number.isFinite(quotaGiB) || quotaGiB <= 0) {
+ return 0
+ }
+ return quotaGiB * 1024 * 1024
+ },
+ bucketUsagePercent () {
+ if (this.bucketQuotaKB <= 0) {
+ return 0
+ }
+ const sizeKB = Number(this.resource?.size || 0)
+ if (!Number.isFinite(sizeKB) || sizeKB <= 0) {
+ return 0
+ }
+ return Math.min(Number(((sizeKB / this.bucketQuotaKB) * 100).toFixed(2)), 100)
+ },
routeFromResourceType () {
return this.$getRouteFromResourceType(this.resource.resourcetype)
},
diff --git a/ui/src/views/compute/DeployVnfAppliance.vue b/ui/src/views/compute/DeployVnfAppliance.vue
index ba9ca733c016..2d4f5fa5c899 100644
--- a/ui/src/views/compute/DeployVnfAppliance.vue
+++ b/ui/src/views/compute/DeployVnfAppliance.vue
@@ -2260,20 +2260,30 @@ export default {
Object.entries(createVnfAppData).filter(([key, value]) => value !== undefined))
var idx = 0
+ const userdataDetailNames = new Set()
if (this.templateUserDataValues) {
for (const [key, value] of Object.entries(this.templateUserDataValues)) {
+ if (!this.isSafeUserDataDetailValue(value)) {
+ continue
+ }
createVnfAppData['userdatadetails[' + idx + '].' + `${key}`] = value
+ userdataDetailNames.add(key)
idx++
}
}
if (isUserdataAllowed && this.userDataValues) {
for (const [key, value] of Object.entries(this.userDataValues)) {
+ if (!this.isSafeUserDataDetailValue(value)) {
+ continue
+ }
createVnfAppData['userdatadetails[' + idx + '].' + `${key}`] = value
+ userdataDetailNames.add(key)
idx++
}
}
+ idx = this.addVnfDetailsToDeployParams(createVnfAppData, idx, userdataDetailNames)
- const httpMethod = createVnfAppData.userdata ? 'POST' : 'GET'
+ const httpMethod = this.hasSensitiveDeployData(createVnfAppData) ? 'POST' : 'GET'
const args = httpMethod === 'POST' ? {} : createVnfAppData
const data = httpMethod === 'POST' ? createVnfAppData : {}
@@ -2289,6 +2299,7 @@ export default {
const name = vm.displayname || vm.name || vm.id
const username = vm.vnfdetails?.username || null
const password = vm.vnfdetails?.password || null
+ const effectivePassword = vm.password || password
const sshUsername = vm.vnfdetails?.ssh_user || null
const sshPassword = vm.vnfdetails?.ssh_password || null
const webUsername = vm.vnfdetails?.web_user || null
@@ -2297,8 +2308,8 @@ export default {
if (username) {
credentials.push(this.$t('label.username') + ' : ' + username)
}
- if (password) {
- credentials.push(this.$t('label.password.default') + ' : ' + password)
+ if (effectivePassword) {
+ credentials.push(this.$t('label.password.default') + ' : ' + effectivePassword)
}
if (webUsername) {
credentials.push('Web ' + this.$t('label.username') + ' : ' + webUsername)
@@ -2312,9 +2323,6 @@ export default {
if (sshPassword) {
credentials.push('SSH ' + this.$t('label.password.default') + ' : ' + sshPassword)
}
- if (vm.password) {
- credentials.push('New password : ' + vm.password)
- }
if (credentials.length > 0) {
credentials.push(this.$t('message.vnf.credentials.change'))
} else {
@@ -2815,6 +2823,38 @@ export default {
handleNicsNetworkSelection (nicToNetworkSelection) {
this.nicToNetworkSelection = nicToNetworkSelection
},
+ getVnfDetailsForDeploy () {
+ const vnfDetails = this.template?.vnfdetails || {}
+ return Object.fromEntries(Object.entries(vnfDetails).filter(([key, value]) => {
+ return key && value !== undefined && value !== null && value !== ''
+ }))
+ },
+ addVnfDetailsToDeployParams (params, idx, existingDetailNames) {
+ const vnfDetails = this.getVnfDetailsForDeploy()
+ const vmPassword = vnfDetails.password || vnfDetails.ssh_password
+ if (!params.password && vmPassword) {
+ params.password = vmPassword
+ }
+ for (const [key, value] of Object.entries(vnfDetails)) {
+ if (existingDetailNames.has(key) || !this.isSafeUserDataDetailValue(value)) {
+ continue
+ }
+ params['userdatadetails[' + idx + '].' + key] = value
+ existingDetailNames.add(key)
+ idx++
+ }
+ return idx
+ },
+ isSafeUserDataDetailValue (value) {
+ // Backend serializes userdata details as Map.toString() and later splits on comma.
+ return !String(value).includes(',')
+ },
+ hasSensitiveDeployData (params) {
+ if (params.userdata || params.password) {
+ return true
+ }
+ return Object.keys(params).some(key => key.startsWith('userdatadetails[') && key.toLowerCase().includes('password'))
+ },
getSelectedNetworksWithExistingConfig (networks) {
for (var i in this.networks) {
var n = this.networks[i]
From e0d6087ad22234f4ec687b59ab182a76178692f3 Mon Sep 17 00:00:00 2001
From: JS Choi <77760789+jschoiRR@users.noreply.github.com>
Date: Wed, 29 Apr 2026 13:03:34 +0900
Subject: [PATCH 2/3] =?UTF-8?q?=EC=83=81=ED=83=9C=20=ED=91=9C=EC=8B=9C=20?=
=?UTF-8?q?=ED=95=AD=EB=AA=A9=EC=A4=91=20created=20=ED=95=AD=EB=AA=A9=20?=
=?UTF-8?q?=EA=B5=AC=EB=B6=84=EB=B0=98=EC=98=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
ui/public/locales/en.json | 1 +
ui/public/locales/ko_KR.json | 1 +
ui/src/components/widgets/Status.vue | 5 ++++-
3 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index fefb8933571a..7b9ad18ade09 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -3890,6 +3890,7 @@
"router.health.checks": "Health check",
"side.by.side": "Side by Side",
"state.completed": "Completed",
+"state.created": "Created",
"state.declined": "Declined",
"state.disabled": "Disabled",
"state.enabled": "Enabled",
diff --git a/ui/public/locales/ko_KR.json b/ui/public/locales/ko_KR.json
index 9aea9efdd825..47696cc1988a 100644
--- a/ui/public/locales/ko_KR.json
+++ b/ui/public/locales/ko_KR.json
@@ -3889,6 +3889,7 @@
"router.health.checks": "\ud5ec\uc2a4 \uccb4\ud06c",
"side.by.side": "\ub098\ub780\ud788",
"state.completed": "\uc644\ub8cc",
+"state.created": "\uc0dd\uc131\ub428",
"state.declined": "\uac70\uc808",
"state.disabled": "\ube44\ud65c\uc131\ud654",
"state.enabled": "\ud65c\uc131\ud654",
diff --git a/ui/src/components/widgets/Status.vue b/ui/src/components/widgets/Status.vue
index 761a569763f2..23b7eed380dd 100644
--- a/ui/src/components/widgets/Status.vue
+++ b/ui/src/components/widgets/Status.vue
@@ -182,6 +182,9 @@ export default {
case 'nodata':
state = this.$t('state.nodata')
break
+ case 'created':
+ state = this.$t('state.created')
+ break
}
return state.charAt(0).toUpperCase() + state.slice(1)
}
@@ -190,6 +193,7 @@ export default {
getBadgeStatus (state) {
var status = 'default'
switch (state.toLowerCase()) {
+ case 'created':
case 'active':
case 'backedup':
case 'completed':
@@ -250,7 +254,6 @@ export default {
status = 'warning'
}
break
- case 'created':
case 'maintenance':
case 'pending':
case 'unsecure':
From fef96362cd502b5007efd6cacacc3774b7e402a2 Mon Sep 17 00:00:00 2001
From: JS Choi <77760789+jschoiRR@users.noreply.github.com>
Date: Wed, 29 Apr 2026 14:25:17 +0900
Subject: [PATCH 3/3] =?UTF-8?q?=EB=B2=84=ED=82=B7=20=EC=82=AC=EC=9A=A9?=
=?UTF-8?q?=EB=9F=89=20=EC=A1=B0=ED=9A=8C=201=EB=B6=84=EC=9D=98=20?=
=?UTF-8?q?=ED=95=9C=EB=B2=88=EC=94=A9=EC=9C=BC=EB=A1=9C=20=EC=88=98?=
=?UTF-8?q?=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../storage/object/BucketApiServiceImpl.java | 30 +++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/server/src/main/java/org/apache/cloudstack/storage/object/BucketApiServiceImpl.java b/server/src/main/java/org/apache/cloudstack/storage/object/BucketApiServiceImpl.java
index ef35e153ec67..8794767574db 100644
--- a/server/src/main/java/org/apache/cloudstack/storage/object/BucketApiServiceImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/storage/object/BucketApiServiceImpl.java
@@ -86,6 +86,7 @@ public boolean configure(String name, Map params) throws Configu
@Override
public boolean start() {
_executor.scheduleWithFixedDelay(new BucketUsageTask(), 60L, 3600L, TimeUnit.SECONDS);
+ _executor.scheduleWithFixedDelay(new BucketUsageCustomTask(), 60L, 60L, TimeUnit.SECONDS);
return true;
}
@@ -294,6 +295,35 @@ public void getBucketUsage() {
}
}
+ private class BucketUsageCustomTask extends ManagedContextRunnable {
+ public BucketUsageCustomTask() {
+ }
+
+ @Override
+ protected void runInContext() {
+ try {
+ List objectStores = _objectStoreDao.listObjectStores();
+ for(ObjectStoreVO objectStoreVO: objectStores) {
+ List buckets = _bucketDao.listByObjectStoreId(objectStoreVO.getId());
+ if (buckets.isEmpty()) {
+ continue;
+ }
+ ObjectStoreEntity objectStore = (ObjectStoreEntity)_dataStoreMgr.getDataStore(objectStoreVO.getId(), DataStoreRole.Object);
+ Map bucketSizes = objectStore.getAllBucketsUsage();
+ for(BucketVO bucket : buckets) {
+ Long size = bucketSizes.get(bucket.getName());
+ if( size != null){
+ bucket.setSize(size);
+ _bucketDao.update(bucket.getId(), bucket);
+ }
+ }
+ }
+ } catch (Exception e) {
+ logger.error("Error while fetching bucket usage", e);
+ }
+ }
+ }
+
private class BucketUsageTask extends ManagedContextRunnable {
public BucketUsageTask() {
}