From 7a7e449af67a1d7e18708b9d32e298cdf84fbf91 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Fri, 3 Sep 2021 09:59:56 +0200 Subject: [PATCH 1/3] usage: fix backup usage (#5259) when creating usage record for backup usages in a period, we need to check if vm offering is changed. if vm offering is changed, create a new cloud usage record, otherwise update existing usage record. This PR fixes #4982 --- .../cloud/usage/parser/BackupUsageParser.java | 68 +++++-------------- 1 file changed, 17 insertions(+), 51 deletions(-) diff --git a/usage/src/main/java/com/cloud/usage/parser/BackupUsageParser.java b/usage/src/main/java/com/cloud/usage/parser/BackupUsageParser.java index bacf706cf17..d0f25c05ffe 100644 --- a/usage/src/main/java/com/cloud/usage/parser/BackupUsageParser.java +++ b/usage/src/main/java/com/cloud/usage/parser/BackupUsageParser.java @@ -17,15 +17,13 @@ package com.cloud.usage.parser; +import java.text.DecimalFormat; import java.util.Date; -import java.util.HashMap; import java.util.List; -import java.util.Map; import javax.annotation.PostConstruct; import javax.inject.Inject; -import org.apache.cloudstack.backup.Backup; import org.apache.cloudstack.usage.UsageTypes; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -68,65 +66,33 @@ public class BackupUsageParser { return true; } - final Map vmUsageMap = new HashMap<>(); for (final UsageBackupVO usageBackup : usageBackups) { final Long vmId = usageBackup.getVmId(); final Long zoneId = usageBackup.getZoneId(); final Long offeringId = usageBackup.getBackupOfferingId(); - if (vmUsageMap.get(vmId) == null) { - vmUsageMap.put(vmId, new BackupUsageParser.BackupInfo(new Backup.Metric(0L, 0L), zoneId, vmId, offeringId)); + Date createdDate = usageBackup.getCreated(); + Date removedDate = usageBackup.getRemoved(); + if (createdDate.before(startDate)) { + createdDate = startDate; } - final Backup.Metric metric = vmUsageMap.get(vmId).getMetric(); - metric.setBackupSize(metric.getBackupSize() + usageBackup.getSize()); - metric.setDataSize(metric.getDataSize() + usageBackup.getProtectedSize()); - } + if (removedDate == null || removedDate.after(endDate)) { + removedDate = endDate; + } + final long duration = (removedDate.getTime() - createdDate.getTime()) + 1; + final float usage = duration / 1000f / 60f / 60f; + DecimalFormat dFormat = new DecimalFormat("#.######"); + String usageDisplay = dFormat.format(usage); - for (final BackupInfo backupInfo : vmUsageMap.values()) { - final Long vmId = backupInfo.getVmId(); - final Long zoneId = backupInfo.getZoneId(); - final Long offeringId = backupInfo.getOfferingId(); - final Double rawUsage = (double) backupInfo.getMetric().getBackupSize(); - final Double sizeGib = rawUsage / (1024.0 * 1024.0 * 1024.0); - final String description = String.format("Backup usage VM ID: %d", vmId); - final String usageDisplay = String.format("%.4f GiB", sizeGib); + final Double rawUsage = (double) usageBackup.getSize(); + final String description = String.format("Backup usage VM ID: %d, backup offering: %d", vmId, offeringId); final UsageVO usageRecord = - new UsageVO(zoneId, account.getAccountId(), account.getDomainId(), description, usageDisplay, - UsageTypes.BACKUP, rawUsage, vmId, null, offeringId, null, vmId, - backupInfo.getMetric().getBackupSize(), backupInfo.getMetric().getDataSize(), startDate, endDate); + new UsageVO(zoneId, account.getAccountId(), account.getDomainId(), description, usageDisplay + " Hrs", + UsageTypes.BACKUP, new Double(usage), vmId, null, offeringId, null, vmId, + usageBackup.getSize(), usageBackup.getProtectedSize(), startDate, endDate); s_usageDao.persist(usageRecord); } return true; } - - static class BackupInfo { - Backup.Metric metric; - Long zoneId; - Long vmId; - Long offeringId; - - public BackupInfo(Backup.Metric metric, Long zoneId, Long vmId, Long offeringId) { - this.metric = metric; - this.zoneId = zoneId; - this.vmId = vmId; - this.offeringId = offeringId; - } - - public Backup.Metric getMetric() { - return metric; - } - - public Long getZoneId() { - return zoneId; - } - - public Long getVmId() { - return vmId; - } - - public Long getOfferingId() { - return offeringId; - } - } } \ No newline at end of file From 0bc6e05bc4c02b041bfa562762bd1a206b7b01ac Mon Sep 17 00:00:00 2001 From: davidjumani Date: Fri, 3 Sep 2021 16:23:26 +0530 Subject: [PATCH 2/3] ui: Honour default.ui.page.size (#5394) * ui: Honour default.ui.page.size * Moving db upgrade path --- .../META-INF/db/schema-41510to41520.sql | 1 + ui/src/store/getters.js | 3 ++- ui/src/store/modules/user.js | 13 ++++++++++++- ui/src/views/AutogenView.vue | 19 +++++++++++++------ 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41510to41520.sql b/engine/schema/src/main/resources/META-INF/db/schema-41510to41520.sql index d0a08c16f78..6698b7dfcaf 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41510to41520.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41510to41520.sql @@ -19,3 +19,4 @@ -- Schema upgrade from 4.15.1.0 to 4.15.2.0 --; +UPDATE `cloud`.`configuration` SET `value` = 20, `default_value` = 20 WHERE `name` = 'default.ui.page.size'; diff --git a/ui/src/store/getters.js b/ui/src/store/getters.js index eda3d9a6b13..b6c1fb58be6 100644 --- a/ui/src/store/getters.js +++ b/ui/src/store/getters.js @@ -39,7 +39,8 @@ const getters = { server: state => state.app.server, domainStore: state => state.user.domainStore, darkMode: state => state.user.darkMode, - themeSetting: state => state.user.themeSetting + themeSetting: state => state.user.themeSetting, + defaultListViewPageSize: state => state.user.defaultListViewPageSize } export default getters diff --git a/ui/src/store/modules/user.js b/ui/src/store/modules/user.js index ce2844899a7..9bd760ba1be 100644 --- a/ui/src/store/modules/user.js +++ b/ui/src/store/modules/user.js @@ -56,7 +56,8 @@ const user = { usebrowsertimezone: false, domainStore: {}, darkMode: false, - themeSetting: {} + themeSetting: {}, + defaultListViewPageSize: 20 }, mutations: { @@ -119,6 +120,9 @@ const user = { SET_THEME_SETTING (state, setting) { state.themeSetting = setting Vue.ls.set(THEME_SETTING, setting) + }, + SET_DEFAULT_LISTVIEW_PAGE_SIZE: (state, defaultListViewPageSize) => { + state.defaultListViewPageSize = defaultListViewPageSize } }, @@ -255,6 +259,13 @@ const user = { reject(error) }) + api('listConfigurations', { name: 'default.ui.page.size' }).then(response => { + const defaultListViewPageSize = parseInt(response.listconfigurationsresponse.configuration[0].value) + commit('SET_DEFAULT_LISTVIEW_PAGE_SIZE', defaultListViewPageSize) + }).catch(error => { + reject(error) + }) + api('listLdapConfigurations').then(response => { const ldapEnable = (response.ldapconfigurationresponse.count > 0) commit('SET_LDAP', ldapEnable) diff --git a/ui/src/views/AutogenView.vue b/ui/src/views/AutogenView.vue index b22cf4e842c..8b61858d0cb 100644 --- a/ui/src/views/AutogenView.vue +++ b/ui/src/views/AutogenView.vue @@ -330,7 +330,7 @@ :pageSize="pageSize" :total="itemCount" :showTotal="total => `${$t('label.showing')} ${Math.min(total, 1+((page-1)*pageSize))}-${Math.min(page*pageSize, total)} ${$t('label.of')} ${total} ${$t('label.items')}`" - :pageSizeOptions="device === 'desktop' ? ['20', '50', '100', '200'] : ['10', '20', '50', '100', '200']" + :pageSizeOptions="pageSizeOptions" @change="changePage" @showSizeChange="changePageSize" showSizeChanger @@ -392,7 +392,7 @@ export default { items: [], itemCount: 0, page: 1, - pageSize: 10, + pageSize: this.$store.getters.defaultListViewPageSize, resource: {}, selectedRowKeys: [], currentAction: {}, @@ -431,9 +431,6 @@ export default { this.execAction(action, isGroupAction) }) - if (this.device === 'desktop') { - this.pageSize = 20 - } this.currentPath = this.$route.fullPath this.fetchData() if ('projectid' in this.$route.query) { @@ -456,7 +453,6 @@ export default { this.pageSize = Number(to.query.pagesize) } else { this.page = 1 - this.pageSize = (this.device === 'desktop' ? 20 : 10) } this.itemCount = 0 this.fetchData() @@ -474,6 +470,17 @@ export default { this.fetchData() } }, + computed: { + pageSizeOptions () { + var sizes = [20, 50, 100, 200, this.$store.getters.defaultListViewPageSize] + if (this.device !== 'desktop') { + sizes.unshift(10) + } + return [...new Set(sizes)].sort(function (a, b) { + return a - b + }).map(String) + } + }, methods: { switchProject (projectId) { if (!projectId || !projectId.length || projectId.length !== 36) { From b2fd1f6d32f418942bbe88d9e924646af1a4272d Mon Sep 17 00:00:00 2001 From: Pearl Dsilva Date: Fri, 3 Sep 2021 16:25:11 +0530 Subject: [PATCH 3/3] server: Allow public templates with no url to be migrated (#5404) This PR allows migration of public templates that are created from snapshots / volumes. Data migration across secondary stores initially excluded all public templates on the pretext that public templates are automatically synced when a new image store is added; however, this assumption isn't true for templates marked as "public" when created from snapshots / volumes. Such templates can be identified if their url is null --- .../engine/orchestration/DataMigrationUtility.java | 3 ++- .../java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtility.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtility.java index 7fad871462d..9d044f3c7da 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtility.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtility.java @@ -179,7 +179,8 @@ public class DataMigrationUtility { List templates = templateDataStoreDao.listByStoreId(srcDataStore.getId()); for (TemplateDataStoreVO template : templates) { VMTemplateVO templateVO = templateDao.findById(template.getTemplateId()); - if (template.getState() == ObjectInDataStoreStateMachine.State.Ready && templateVO != null && !templateVO.isPublicTemplate() && + if (template.getState() == ObjectInDataStoreStateMachine.State.Ready && templateVO != null && + (!templateVO.isPublicTemplate() || (templateVO.isPublicTemplate() && templateVO.getUrl() == null)) && templateVO.getHypervisorType() != Hypervisor.HypervisorType.Simulator) { files.add(templateFactory.getTemplate(template.getTemplateId(), srcDataStore)); } diff --git a/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java index d80786352bf..3739b068c8d 100644 --- a/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java @@ -122,7 +122,11 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation