Skip to content

Commit 4e7236c

Browse files
committed
Backfill network rate for existing NICs/networks and refresh it on migration
Add an upgrade-time backfill for nics.network_rate and the network_details networkrate entry, frozen to pre-feature precedence so existing bandwidth doesn't change. Also refresh network_details rate during migrateGuestNetwork, and add unit tests for getNetworkRate precedence.
1 parent 71d49b8 commit 4e7236c

4 files changed

Lines changed: 445 additions & 0 deletions

File tree

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
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+
package com.cloud.upgrade;
18+
19+
import java.sql.PreparedStatement;
20+
import java.sql.ResultSet;
21+
import java.sql.SQLException;
22+
import java.util.List;
23+
24+
import org.apache.logging.log4j.LogManager;
25+
import org.apache.logging.log4j.Logger;
26+
27+
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
28+
import org.apache.cloudstack.framework.config.dao.ConfigurationDaoImpl;
29+
30+
import com.cloud.dc.dao.DataCenterDetailsDaoImpl;
31+
import com.cloud.network.Networks.TrafficType;
32+
import com.cloud.network.dao.NetworkDao;
33+
import com.cloud.network.dao.NetworkDaoImpl;
34+
import com.cloud.network.dao.NetworkDetailsDao;
35+
import com.cloud.network.dao.NetworkDetailsDaoImpl;
36+
import com.cloud.network.dao.NetworkVO;
37+
import com.cloud.service.ServiceOfferingVO;
38+
import com.cloud.service.dao.ServiceOfferingDao;
39+
import com.cloud.service.dao.ServiceOfferingDaoImpl;
40+
import com.cloud.utils.db.TransactionLegacy;
41+
import com.cloud.vm.NicVO;
42+
import com.cloud.vm.VMInstanceVO;
43+
import com.cloud.vm.VirtualMachine;
44+
import com.cloud.vm.dao.NicDao;
45+
import com.cloud.vm.dao.NicDaoImpl;
46+
import com.cloud.vm.dao.VMInstanceDao;
47+
import com.cloud.vm.dao.VMInstanceDaoImpl;
48+
49+
/**
50+
* Backfills {@code nics.network_rate} and the {@code network_details} "networkrate" entry for
51+
* pre-existing NICs/networks. Deliberately frozen to the pre-feature precedence of
52+
* {@link com.cloud.network.NetworkModelImpl#getNetworkRate} - do not redirect this to call the
53+
* live method, whose precedence will keep evolving.
54+
*/
55+
public class NetworkRateBackfill {
56+
protected static Logger LOGGER = LogManager.getLogger(NetworkRateBackfill.class);
57+
58+
private static final String CONFIG_NETWORK_THROTTLING_RATE = "network.throttling.rate";
59+
private static final String CONFIG_VM_NETWORK_THROTTLING_RATE = "vm.network.throttling.rate";
60+
private static final String NETWORKRATE_DETAIL_NAME = "networkrate";
61+
private static final int DEFAULT_THROTTLING_RATE = 200;
62+
63+
private final NicDao nicDao = new NicDaoImpl();
64+
private final VMInstanceDao vmInstanceDao = new VMInstanceDaoImpl();
65+
private final NetworkDao networkDao = new NetworkDaoImpl();
66+
private final NetworkDetailsDao networkDetailsDao = new NetworkDetailsDaoImpl();
67+
private final ServiceOfferingDao serviceOfferingDao = new ServiceOfferingDaoImpl();
68+
private final DataCenterDetailsDaoImpl dataCenterDetailsDao = new DataCenterDetailsDaoImpl();
69+
private final ConfigurationDao configurationDao = new ConfigurationDaoImpl();
70+
71+
public void backfillNetworkRates() {
72+
backfillNicNetworkRates();
73+
backfillNetworkDetailsRates();
74+
}
75+
76+
private void backfillNicNetworkRates() {
77+
final String sql = "SELECT id, network_id, instance_id, default_nic FROM nics " +
78+
"WHERE removed IS NULL AND network_rate IS NULL AND instance_id IS NOT NULL";
79+
try (PreparedStatement pstmt = TransactionLegacy.currentTxn().prepareAutoCloseStatement(sql);
80+
ResultSet rs = pstmt.executeQuery()) {
81+
while (rs.next()) {
82+
final long nicId = rs.getLong("id");
83+
final long networkId = rs.getLong("network_id");
84+
final long instanceId = rs.getLong("instance_id");
85+
final boolean defaultNic = rs.getBoolean("default_nic");
86+
try {
87+
final Integer rate = computeLegacyNicNetworkRate(networkId, instanceId, defaultNic);
88+
if (rate != null && rate > 0) {
89+
updateNicNetworkRate(nicId, rate);
90+
}
91+
} catch (Exception e) {
92+
LOGGER.warn("Failed to backfill network_rate for nic id=" + nicId + ": " + e.getMessage());
93+
}
94+
}
95+
} catch (SQLException e) {
96+
LOGGER.warn("Failed to backfill nic network rates: " + e.getMessage());
97+
}
98+
}
99+
100+
private void updateNicNetworkRate(long nicId, int rate) throws SQLException {
101+
try (PreparedStatement pstmt = TransactionLegacy.currentTxn().prepareAutoCloseStatement(
102+
"UPDATE nics SET network_rate = ? WHERE id = ?")) {
103+
pstmt.setInt(1, rate);
104+
pstmt.setLong(2, nicId);
105+
pstmt.executeUpdate();
106+
}
107+
}
108+
109+
private Integer computeLegacyNicNetworkRate(long networkId, long instanceId, boolean defaultNic) {
110+
final NetworkVO network = networkDao.findById(networkId);
111+
if (network == null) {
112+
return null;
113+
}
114+
final VMInstanceVO vm = vmInstanceDao.findById(instanceId);
115+
if (vm != null) {
116+
if (vm.getType() == VirtualMachine.Type.User) {
117+
if (defaultNic) {
118+
return getServiceOfferingNetworkRate(vm.getServiceOfferingId(), network.getDataCenterId());
119+
}
120+
} else if (vm.getType() == VirtualMachine.Type.DomainRouter) {
121+
if (TrafficType.Guest.equals(network.getTrafficType())) {
122+
return getNetworkOfferingNetworkRate(network.getNetworkOfferingId(), network.getDataCenterId());
123+
} else if (TrafficType.Public.equals(network.getTrafficType())) {
124+
final Integer rate = findRouterGuestNetworkRate(vm.getId(), network.getDataCenterId());
125+
if (rate != null) {
126+
return rate;
127+
}
128+
}
129+
} else if (vm.getType() == VirtualMachine.Type.ConsoleProxy || vm.getType() == VirtualMachine.Type.SecondaryStorageVm) {
130+
return -1;
131+
}
132+
}
133+
return getNetworkOfferingNetworkRate(network.getNetworkOfferingId(), network.getDataCenterId());
134+
}
135+
136+
private Integer findRouterGuestNetworkRate(long routerInstanceId, long dataCenterId) {
137+
final List<NicVO> routerNics = nicDao.listByVmId(routerInstanceId);
138+
for (final NicVO routerNic : routerNics) {
139+
final NetworkVO nw = networkDao.findById(routerNic.getNetworkId());
140+
if (nw != null && TrafficType.Guest.equals(nw.getTrafficType())) {
141+
return getNetworkOfferingNetworkRate(nw.getNetworkOfferingId(), dataCenterId);
142+
}
143+
}
144+
return null;
145+
}
146+
147+
private void backfillNetworkDetailsRates() {
148+
final String sql = "SELECT n.id, n.network_offering_id, n.data_center_id FROM networks n " +
149+
"WHERE n.removed IS NULL AND NOT EXISTS " +
150+
"(SELECT 1 FROM network_details d WHERE d.network_id = n.id AND d.name = ?)";
151+
try (PreparedStatement pstmt = TransactionLegacy.currentTxn().prepareAutoCloseStatement(sql)) {
152+
pstmt.setString(1, NETWORKRATE_DETAIL_NAME);
153+
try (ResultSet rs = pstmt.executeQuery()) {
154+
while (rs.next()) {
155+
final long networkId = rs.getLong("id");
156+
final long networkOfferingId = rs.getLong("network_offering_id");
157+
final long dataCenterId = rs.getLong("data_center_id");
158+
try {
159+
final int rate = getNetworkOfferingNetworkRate(networkOfferingId, dataCenterId);
160+
networkDetailsDao.addDetail(networkId, NETWORKRATE_DETAIL_NAME, String.valueOf(rate), true);
161+
} catch (Exception e) {
162+
LOGGER.warn("Failed to backfill network_details rate for network id=" + networkId + ": " + e.getMessage());
163+
}
164+
}
165+
}
166+
} catch (SQLException e) {
167+
LOGGER.warn("Failed to backfill network details rates: " + e.getMessage());
168+
}
169+
}
170+
171+
private int getServiceOfferingNetworkRate(long serviceOfferingId, long dataCenterId) {
172+
final ServiceOfferingVO offering = serviceOfferingDao.findById(serviceOfferingId);
173+
Integer rate = offering == null ? null : offering.getRateMbps();
174+
if (rate == null) {
175+
final String vmType = offering == null ? null : offering.getVmType();
176+
final String configName = "DomainRouter".equalsIgnoreCase(vmType) ? CONFIG_NETWORK_THROTTLING_RATE : CONFIG_VM_NETWORK_THROTTLING_RATE;
177+
rate = getZoneScopedConfigValue(configName, dataCenterId);
178+
}
179+
return normalizeRate(rate);
180+
}
181+
182+
private int getNetworkOfferingNetworkRate(long networkOfferingId, long dataCenterId) {
183+
Integer rate = getNetworkOfferingRateMbps(networkOfferingId);
184+
if (rate == null) {
185+
rate = getZoneScopedConfigValue(CONFIG_NETWORK_THROTTLING_RATE, dataCenterId);
186+
}
187+
return normalizeRate(rate);
188+
}
189+
190+
// NetworkOfferingDaoImpl's constructor is protected, so it can't be instantiated here directly.
191+
private Integer getNetworkOfferingRateMbps(long networkOfferingId) {
192+
try (PreparedStatement pstmt = TransactionLegacy.currentTxn().prepareAutoCloseStatement(
193+
"SELECT nw_rate FROM network_offerings WHERE id = ?")) {
194+
pstmt.setLong(1, networkOfferingId);
195+
try (ResultSet rs = pstmt.executeQuery()) {
196+
if (rs.next()) {
197+
final Object nwRate = rs.getObject(1);
198+
return nwRate == null ? null : ((Number) nwRate).intValue();
199+
}
200+
}
201+
} catch (SQLException e) {
202+
LOGGER.warn("Failed to read nw_rate for network offering id=" + networkOfferingId + ": " + e.getMessage());
203+
}
204+
return null;
205+
}
206+
207+
private int normalizeRate(int rate) {
208+
return rate == 0 ? -1 : rate;
209+
}
210+
211+
private int getZoneScopedConfigValue(String name, long dataCenterId) {
212+
final String zoneValue = dataCenterDetailsDao.getConfigValue(dataCenterId, name);
213+
final String value = zoneValue != null ? zoneValue : configurationDao.getValue(name);
214+
return value != null ? Integer.parseInt(value) : DEFAULT_THROTTLING_RATE;
215+
}
216+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.io.InputStream;
2020
import java.sql.Connection;
2121

22+
import com.cloud.upgrade.NetworkRateBackfill;
2223
import com.cloud.utils.exception.CloudRuntimeException;
2324

2425
public class Upgrade42300to42400 extends DbUpgradeAbstractImpl implements DbUpgrade {
@@ -45,6 +46,7 @@ public InputStream[] getPrepareScripts() {
4546

4647
@Override
4748
public void performDataMigration(Connection conn) {
49+
new NetworkRateBackfill().backfillNetworkRates();
4850
}
4951

5052
@Override

server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.apache.logging.log4j.Logger;
2525
import org.apache.logging.log4j.LogManager;
2626

27+
import org.apache.cloudstack.api.ApiConstants;
2728
import org.apache.cloudstack.context.CallContext;
2829
import org.apache.cloudstack.engine.cloud.entity.api.db.VMNetworkMapVO;
2930
import org.apache.cloudstack.engine.cloud.entity.api.db.dao.VMNetworkMapDao;
@@ -484,6 +485,8 @@ public Network upgradeNetworkToNewNetworkOffering(long networkId, long newPhysic
484485
network.setVpcId(vpcId);
485486
}
486487
_networksDao.update(network.getId(), network, _networkMgr.finalizeServicesAndProvidersForNetwork(_entityMgr.findById(NetworkOffering.class, networkOfferingId), newPhysicalNetworkId));
488+
Integer networkRate = _networkModel.getNetworkRate(network.getId(), null);
489+
_networkDetailsDao.addDetail(network.getId(), ApiConstants.NETWORKRATE, String.valueOf(networkRate), true);
487490
return network;
488491
}
489492

0 commit comments

Comments
 (0)