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() { } 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/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 @@ +
+
사용량
+
+ + {{ toSize(Number(resource.size || 0)) }} / {{ resource.quota }} GiB +
+
+ +
+
{{ $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/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': 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]