Skip to content

Commit 2ca164a

Browse files
GutoVeroneziGutoVeroneziDaanHoogland
authored
Quota custom tariffs (#5909)
Co-authored-by: GutoVeronezi <daniel@scclouds.com.br> Co-authored-by: dahn <daan.hoogland@gmail.com>
1 parent 289a43f commit 2ca164a

File tree

89 files changed

+5948
-694
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+5948
-694
lines changed

api/src/main/java/org/apache/cloudstack/api/ApiConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public class ApiConstants {
2323
public static final String ACCOUNT_ID = "accountid";
2424
public static final String ACCOUNT_IDS = "accountids";
2525
public static final String ACCUMULATE = "accumulate";
26+
public static final String ACTIVATION_RULE = "activationrule";
2627
public static final String ACTIVITY = "activity";
2728
public static final String ADAPTER_TYPE = "adaptertype";
2829
public static final String ADDRESS = "address";
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.apache.cloudstack.usage;
19+
20+
public enum UsageUnitTypes {
21+
COMPUTE_MONTH ("Compute*Month"),
22+
IP_MONTH ("IP*Month"),
23+
GB ("GB"),
24+
GB_MONTH ("GB*Month"),
25+
POLICY_MONTH ("Policy*Month");
26+
27+
private final String description;
28+
29+
private UsageUnitTypes(String description) {
30+
this.description = description;
31+
}
32+
33+
@Override
34+
public String toString() {
35+
return description;
36+
}
37+
38+
/**
39+
* Retrieves the UsageUnitTypes according to the parameter.<br/><br/>
40+
* If there are no UsageUnitTypes with the description, it will try to retrieve it with {@link UsageUnitTypes#valueOf(String)} and will throw an
41+
* {@link IllegalArgumentException} if it not exist.
42+
*/
43+
public static UsageUnitTypes getByDescription(String description) {
44+
for (UsageUnitTypes type : UsageUnitTypes.values()) {
45+
if (type.toString().equals(description)) {
46+
return type;
47+
}
48+
}
49+
50+
return UsageUnitTypes.valueOf(description);
51+
}
52+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.apache.cloudstack.usage;
19+
20+
import java.util.Arrays;
21+
import java.util.List;
22+
23+
import org.junit.Assert;
24+
import org.junit.Test;
25+
import org.junit.runner.RunWith;
26+
import org.mockito.junit.MockitoJUnitRunner;
27+
28+
@RunWith(MockitoJUnitRunner.class)
29+
public class UsageUnitTypesTest {
30+
31+
private List<UsageUnitTypes> usageUnitTypes = Arrays.asList(UsageUnitTypes.values());
32+
33+
@Test
34+
public void getByDescriptionTestAllTheDescriptionsMustReturnUsageUnitTypes() {
35+
usageUnitTypes.forEach(type -> {
36+
UsageUnitTypes usageUnitType = UsageUnitTypes.getByDescription(type.toString());
37+
Assert.assertEquals(type, usageUnitType);
38+
});
39+
}
40+
41+
@Test
42+
public void getByDescriptionTestAllTheConstantNamesMustReturnUsageUnitTypes() {
43+
usageUnitTypes.forEach(type -> {
44+
UsageUnitTypes usageUnitType = UsageUnitTypes.getByDescription(type.name());
45+
Assert.assertEquals(type, usageUnitType);
46+
});
47+
}
48+
49+
@Test (expected = IllegalArgumentException.class)
50+
public void getByDescriptionTestPassWrongTypeOrDescriptionAndThrowsIllegalArgumentException() {
51+
UsageUnitTypes.getByDescription("test");
52+
}
53+
}

engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotInfo.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,16 @@
1818

1919
import java.util.List;
2020

21+
import org.apache.cloudstack.framework.config.ConfigKey;
22+
2123
import com.cloud.storage.Snapshot;
2224
import com.cloud.utils.exception.CloudRuntimeException;
2325

2426
public interface SnapshotInfo extends DataObject, Snapshot {
27+
ConfigKey<Boolean> BackupSnapshotAfterTakingSnapshot = new ConfigKey<>(Boolean.class, "snapshot.backup.to.secondary", "Snapshots", "true", "Indicates whether to always"
28+
+ " backup primary storage snapshot to secondary storage. Keeping snapshots only on Primary storage is applicable for KVM + Ceph only.", false, ConfigKey.Scope.Global,
29+
null);
30+
2531
SnapshotInfo getParent();
2632

2733
String getPath();

engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -625,8 +625,6 @@ protected void advanceExpunge(VMInstanceVO vm) throws ResourceUnavailableExcepti
625625
final VirtualMachineGuru guru = getVmGuru(vm);
626626
guru.finalizeExpunge(vm);
627627

628-
userVmDetailsDao.removeDetails(vm.getId());
629-
630628
userVmDeployAsIsDetailsDao.removeDetails(vm.getId());
631629

632630
// Remove comments (if any)

engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,6 @@
126126
import com.cloud.storage.dao.VMTemplateDetailsDao;
127127
import com.cloud.storage.dao.VolumeDao;
128128
import com.cloud.storage.dao.VolumeDetailsDao;
129-
import com.cloud.storage.snapshot.SnapshotManager;
130129
import com.cloud.template.TemplateManager;
131130
import com.cloud.template.VirtualMachineTemplate;
132131
import com.cloud.user.Account;
@@ -243,7 +242,7 @@ public enum UserVmCloneType {
243242
private final StateMachine2<Volume.State, Volume.Event, Volume> _volStateMachine;
244243
protected List<StoragePoolAllocator> _storagePoolAllocators;
245244

246-
protected boolean backupSnapshotAfterTakingSnapshot = SnapshotManager.BackupSnapshotAfterTakingSnapshot.value();
245+
protected boolean backupSnapshotAfterTakingSnapshot = SnapshotInfo.BackupSnapshotAfterTakingSnapshot.value();
247246

248247
public List<StoragePoolAllocator> getStoragePoolAllocators() {
249248
return _storagePoolAllocators;

engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41610to41700.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import com.cloud.upgrade.SystemVmTemplateRegistration;
2020
import com.cloud.utils.exception.CloudRuntimeException;
21+
2122
import org.apache.log4j.Logger;
2223

2324
import java.io.InputStream;
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package com.cloud.upgrade.dao;
19+
20+
import com.cloud.upgrade.SystemVmTemplateRegistration;
21+
import com.cloud.utils.exception.CloudRuntimeException;
22+
import org.apache.cloudstack.api.response.UsageTypeResponse;
23+
import org.apache.cloudstack.usage.UsageTypes;
24+
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
25+
import org.apache.commons.lang3.time.DateUtils;
26+
import org.apache.log4j.Logger;
27+
28+
import java.io.InputStream;
29+
import java.sql.Connection;
30+
import java.sql.PreparedStatement;
31+
import java.sql.ResultSet;
32+
import java.sql.SQLException;
33+
import java.util.Arrays;
34+
import java.util.Date;
35+
import java.util.LinkedHashMap;
36+
import java.util.List;
37+
import java.util.Map;
38+
39+
public class Upgrade41700to41800 implements DbUpgrade, DbUpgradeSystemVmTemplate {
40+
41+
final static Logger LOG = Logger.getLogger(Upgrade41700to41800.class);
42+
private SystemVmTemplateRegistration systemVmTemplateRegistration;
43+
44+
@Override
45+
public String[] getUpgradableVersionRange() {
46+
return new String[] {"4.17.0.0", "4.18.0.0"};
47+
}
48+
49+
@Override
50+
public String getUpgradedVersion() {
51+
return "4.18.0.0";
52+
}
53+
54+
@Override
55+
public boolean supportsRollingUpgrade() {
56+
return false;
57+
}
58+
59+
@Override
60+
public InputStream[] getPrepareScripts() {
61+
final String scriptFile = "META-INF/db/schema-41700to41800.sql";
62+
final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile);
63+
if (script == null) {
64+
throw new CloudRuntimeException("Unable to find " + scriptFile);
65+
}
66+
67+
return new InputStream[] {script};
68+
}
69+
70+
@Override
71+
public void performDataMigration(Connection conn) {
72+
convertQuotaTariffsToNewParadigm(conn);
73+
convertVmResourcesQuotaTypesToRunningVmQuotaType(conn);
74+
}
75+
76+
@Override
77+
public InputStream[] getCleanupScripts() {
78+
final String scriptFile = "META-INF/db/schema-41700to41800-cleanup.sql";
79+
final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile);
80+
if (script == null) {
81+
throw new CloudRuntimeException("Unable to find " + scriptFile);
82+
}
83+
84+
return new InputStream[] {script};
85+
}
86+
87+
private void initSystemVmTemplateRegistration() {
88+
systemVmTemplateRegistration = new SystemVmTemplateRegistration();
89+
}
90+
91+
@Override
92+
public void updateSystemVmTemplates(Connection conn) {
93+
LOG.debug("Updating System Vm template IDs");
94+
initSystemVmTemplateRegistration();
95+
try {
96+
systemVmTemplateRegistration.updateSystemVmTemplates(conn);
97+
} catch (Exception e) {
98+
throw new CloudRuntimeException("Failed to find / register SystemVM template(s)");
99+
}
100+
}
101+
102+
protected void convertQuotaTariffsToNewParadigm(Connection conn) {
103+
LOG.info("Converting quota tariffs to new paradigm.");
104+
105+
List<UsageTypeResponse> usageTypeResponses = UsageTypes.listUsageTypes();
106+
107+
for (UsageTypeResponse usageTypeResponse : usageTypeResponses) {
108+
Integer usageType = usageTypeResponse.getUsageType();
109+
110+
String tariffTypeDescription = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(usageTypeResponse, "description", "usageType");
111+
112+
LOG.info(String.format("Converting quota tariffs of type %s to new paradigm.", tariffTypeDescription));
113+
114+
for (boolean previousTariff : Arrays.asList(true, false)) {
115+
Map<Long, Date> tariffs = selectTariffs(conn, usageType, previousTariff, tariffTypeDescription);
116+
117+
int tariffsSize = tariffs.size();
118+
if (tariffsSize < 2) {
119+
LOG.info(String.format("Quota tariff of type %s has [%s] %s register(s). Tariffs with less than 2 register do not need to be converted to new paradigm.",
120+
tariffTypeDescription, tariffsSize, previousTariff ? "previous of current" : "next to current"));
121+
continue;
122+
}
123+
124+
executeUpdateQuotaTariffSetEndDateAndRemoved(conn, usageType, tariffs, previousTariff, tariffTypeDescription);
125+
}
126+
}
127+
}
128+
129+
protected Map<Long, Date> selectTariffs(Connection conn, Integer usageType, boolean previousTariff, String tariffTypeDescription) {
130+
Map<Long, Date> quotaTariffs = new LinkedHashMap<>();
131+
132+
String selectQuotaTariffs = String.format("SELECT id, effective_on FROM cloud_usage.quota_tariff WHERE %s AND usage_type = ? ORDER BY effective_on, updated_on;",
133+
previousTariff ? "usage_name = name" : "removed is null");
134+
135+
LOG.info(String.format("Selecting %s quota tariffs of type [%s] according to SQL [%s].", previousTariff ? "previous of current" : "next to current",
136+
tariffTypeDescription, selectQuotaTariffs));
137+
138+
try (PreparedStatement pstmt = conn.prepareStatement(selectQuotaTariffs)) {
139+
pstmt.setInt(1, usageType);
140+
141+
try (ResultSet result = pstmt.executeQuery()) {
142+
while (result.next()) {
143+
quotaTariffs.put(result.getLong("id"), result.getDate("effective_on"));
144+
}
145+
}
146+
return quotaTariffs;
147+
} catch (SQLException e) {
148+
String message = String.format("Unable to retrieve %s quota tariffs of type [%s] due to [%s].", previousTariff ? "previous" : "next", tariffTypeDescription,
149+
e.getMessage());
150+
LOG.error(message, e);
151+
throw new CloudRuntimeException(message, e);
152+
}
153+
}
154+
155+
protected void executeUpdateQuotaTariffSetEndDateAndRemoved(Connection conn, Integer usageType, Map<Long, Date> tariffs, boolean setRemoved, String tariffTypeDescription) {
156+
String updateQuotaTariff = String.format("UPDATE cloud_usage.quota_tariff SET end_date = ? %s WHERE id = ?;", setRemoved ? ", removed = ?" : "");
157+
158+
Object[] ids = tariffs.keySet().toArray();
159+
160+
LOG.info(String.format("Updating %s registers of %s quota tariffs of type [%s] with SQL [%s].", tariffs.size() -1, setRemoved ? "previous of current" :
161+
"next to current", tariffTypeDescription, updateQuotaTariff));
162+
163+
for (int i = 0; i < tariffs.size() - 1; i++) {
164+
Long id = Long.valueOf(String.valueOf(ids[i]));
165+
Long nextId = Long.valueOf(String.valueOf(ids[i + 1]));
166+
167+
Date endDate = tariffs.get(nextId);
168+
169+
if (!DateUtils.isSameDay(endDate, tariffs.get(id))) {
170+
endDate = DateUtils.addDays(endDate, -1);
171+
}
172+
173+
try (PreparedStatement pstmt = conn.prepareStatement(updateQuotaTariff)) {
174+
java.sql.Date sqlEndDate = new java.sql.Date(endDate.getTime());
175+
pstmt.setDate(1, sqlEndDate);
176+
177+
String updateRemoved = "";
178+
if (setRemoved) {
179+
pstmt.setDate(2, sqlEndDate);
180+
pstmt.setLong(3, id);
181+
182+
updateRemoved = String.format("and \"removed\" to [%s] ", sqlEndDate);
183+
} else {
184+
pstmt.setLong(2, id);
185+
}
186+
187+
LOG.info(String.format("Updating \"end_date\" to [%s] %sof quota tariff with ID [%s].", sqlEndDate, updateRemoved, id));
188+
pstmt.executeUpdate();
189+
} catch (SQLException e) {
190+
String message = String.format("Unable to update \"end_date\" %s of quota tariffs of usage type [%s] due to [%s].", setRemoved ? "and \"removed\"" : "",
191+
usageType, e.getMessage());
192+
LOG.error(message, e);
193+
throw new CloudRuntimeException(message, e);
194+
}
195+
}
196+
}
197+
198+
protected void convertVmResourcesQuotaTypesToRunningVmQuotaType(Connection conn) {
199+
LOG.info("Converting quota tariffs of type \"vCPU\", \"CPU_SPEED\" and \"MEMORY\" to \"RUNNING_VM\".");
200+
201+
String insertSql = String.format("INSERT INTO cloud_usage.quota_tariff (usage_type, usage_name, usage_unit, usage_discriminator, currency_value, effective_on, updated_on,"
202+
+ " updated_by, uuid, name, description, removed, end_date, activation_rule)\n"
203+
+ "SELECT 1, 'RUNNING_VM', usage_unit, '', 0, effective_on, updated_on, updated_by, UUID(), name, description, removed, end_date,\n"
204+
+ " CASE\n"
205+
+ " WHEN usage_type = 15 THEN CONCAT('((value.computingResources ? (value.computingResources.cpuSpeed * value.computingResources.cpuNumber) : 0) / 100) * ', currency_value)\n"
206+
+ " WHEN usage_type = 16 THEN CONCAT('(value.computingResources ? value.computingResources.cpuNumber : 0) * ', currency_value)\n"
207+
+ " WHEN usage_type = 17 THEN CONCAT('(value.computingResources ? value.computingResources.memory : 0)* ', currency_value)\n"
208+
+ " END\n"
209+
+ "FROM cloud_usage.quota_tariff \n"
210+
+ "WHERE usage_type in (15, 16, 17) \n"
211+
+ "AND currency_value > 0.0;");
212+
213+
try (PreparedStatement pstmt = conn.prepareStatement(insertSql)) {
214+
pstmt.executeUpdate();
215+
} catch (SQLException e) {
216+
String message = String.format("Failed to convert quota tariffs of type \"vCPU\", \"CPU_SPEED\" and \"MEMORY\" to \"RUNNING_VM\" due to [%s].", e.getMessage());
217+
LOG.error(message, e);
218+
throw new CloudRuntimeException(message, e);
219+
}
220+
221+
LOG.info("Disabling unused quota tariffs of type \"vCPU\", \"CPU_SPEED\" and \"MEMORY\".");
222+
223+
String updateSql = "UPDATE cloud_usage.quota_tariff SET removed = now() WHERE usage_type in (15, 16, 17) and removed is null;";
224+
225+
try (PreparedStatement pstmt = conn.prepareStatement(updateSql)) {
226+
pstmt.executeUpdate();
227+
} catch (SQLException e) {
228+
String message = String.format("Failed disable quota tariffs of type \"vCPU\", \"CPU_SPEED\" and \"MEMORY\" due to [%s].", e.getMessage());
229+
LOG.error(message, e);
230+
throw new CloudRuntimeException(message, e);
231+
}
232+
}
233+
}

0 commit comments

Comments
 (0)