From 498b69b610da7f928c150e8a08bc4d4ea0e4fe69 Mon Sep 17 00:00:00 2001 From: Manoj Kumar Date: Wed, 3 Jun 2026 22:53:01 +0530 Subject: [PATCH 1/5] persist and expose effective network rate for NIC, Network and compute offering --- .../apache/cloudstack/api/ApiConstants.java | 1 + .../api/response/NetworkResponse.java | 12 +++++++++++ .../cloudstack/api/response/NicResponse.java | 12 +++++++++++ .../orchestration/NetworkOrchestrator.java | 7 +++++++ .../main/java/com/cloud/api/ApiDBUtils.java | 10 +++++++++ .../java/com/cloud/api/ApiResponseHelper.java | 11 ++++++++++ .../api/query/dao/UserVmJoinDaoImpl.java | 10 +++++++++ .../com/cloud/network/NetworkServiceImpl.java | 3 +++ .../java/com/cloud/vm/UserVmManagerImpl.java | 21 ++++++++++++++++++- ui/src/config/section/network.js | 2 +- ui/src/config/section/offering.js | 4 ++-- ui/src/views/network/NicsTable.vue | 3 +++ 12 files changed, 92 insertions(+), 4 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 694830ea2f36..29064df8af88 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -1190,6 +1190,7 @@ public class ApiConstants { public static final String NETSCALER_CONTROLCENTER_ID = "netscalercontrolcenterid"; public static final String NETSCALER_SERVICEPACKAGE_ID = "netscalerservicepackageid"; public static final String FETCH_ROUTER_HEALTH_CHECK_RESULTS = "fetchhealthcheckresults"; + public static final String UNLIMITED = "unlimited"; public static final String ZONE_ID_LIST = "zoneids"; public static final String DESTINATION_ZONE_ID_LIST = "destzoneids"; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java index 3a3663af2551..bc72dda0e068 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java @@ -311,6 +311,10 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement @Param(description = "MTU configured on the network VR's private interfaces") private Integer privateMtu; + @SerializedName(ApiConstants.NETWORKRATE) + @Param(description = "Network rate (in Mb/s) configured for the Guest interface of this network", since = "4.24.0") + private String networkRate; + @SerializedName(ApiConstants.IP6_DNS1) @Param(description = "The first IPv6 DNS for the network", since = "4.18.0") private String ipv6Dns1; @@ -699,6 +703,14 @@ public void setPrivateMtu(Integer privateMtu) { this.privateMtu = privateMtu; } + public String getNetworkRate() { + return networkRate; + } + + public void setNetworkRate(String networkRate) { + this.networkRate = networkRate; + } + public void setIpv6Dns1(String ipv6Dns1) { this.ipv6Dns1 = ipv6Dns1; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NicResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NicResponse.java index 92f25e370fb4..fcbb6ac17d11 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NicResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NicResponse.java @@ -138,6 +138,10 @@ public class NicResponse extends BaseResponse { @Param(description = "MTU configured on the NIC", since="4.18.0") private Integer mtu; + @SerializedName(ApiConstants.NETWORKRATE) + @Param(description = "Network rate (in Mb/s) configured for the NIC", since = "4.24.0") + private String networkRate; + @SerializedName(ApiConstants.PUBLIC_IP_ID) @Param(description = "Public IP address ID associated with this NIC via Static NAT rule") private String publicIpId; @@ -409,6 +413,14 @@ public void setMtu(Integer mtu) { this.mtu = mtu; } + public String getNetworkRate() { + return networkRate; + } + + public void setNetworkRate(String networkRate) { + this.networkRate = networkRate; + } + public String getVpcId() { return vpcId; } diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index 4262ee701aab..9b99f8a4a0d9 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -744,6 +744,12 @@ private void updateRouterIpInNetworkDetails(Long networkId, String routerIp, Str } } + private void saveNetworkRateInDetails(long networkId, NetworkOffering offering, long dataCenterId) { + Integer rate = _configMgr.getNetworkOfferingNetworkRate(offering.getId(), dataCenterId); + String networkRate = (rate == null || rate <= 0) ? ApiConstants.UNLIMITED : String.valueOf(rate); + networkDetailsDao.addDetail(networkId, ApiConstants.NETWORKRATE, networkRate, true); + } + @Override public List setupNetwork(final Account owner, final NetworkOffering offering, final DeploymentPlan plan, final String name, final String displayText, final boolean isDefault) throws ConcurrentOperationException { @@ -819,6 +825,7 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { } updateRouterIpInNetworkDetails(networkPersisted.getId(), network.getRouterIp(), network.getRouterIpv6()); + saveNetworkRateInDetails(networkPersisted.getId(), offering, plan.getDataCenterId()); if (predefined instanceof NetworkVO && guru instanceof NetworkGuruAdditionalFunctions) { final NetworkGuruAdditionalFunctions functions = (NetworkGuruAdditionalFunctions) guru; diff --git a/server/src/main/java/com/cloud/api/ApiDBUtils.java b/server/src/main/java/com/cloud/api/ApiDBUtils.java index 1d00e9ec16ba..4a40fcd42067 100644 --- a/server/src/main/java/com/cloud/api/ApiDBUtils.java +++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java @@ -338,6 +338,7 @@ import com.cloud.vm.DomainRouterVO; import com.cloud.vm.InstanceGroup; import com.cloud.vm.InstanceGroupVO; +import com.cloud.vm.NicDetailVO; import com.cloud.vm.NicProfile; import com.cloud.vm.NicVO; import com.cloud.vm.VMInstanceDetailVO; @@ -351,6 +352,7 @@ import com.cloud.vm.dao.ConsoleProxyDao; import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.NicDetailsDao; import com.cloud.vm.dao.NicSecondaryIpDao; import com.cloud.vm.dao.NicSecondaryIpVO; import com.cloud.vm.dao.UserVmDao; @@ -496,6 +498,7 @@ public class ApiDBUtils { static BackupOfferingDao s_backupOfferingDao; static BackupRepositoryDao s_backupRepositoryDao; static NicDao s_nicDao; + static NicDetailsDao s_nicDetailsDao; static ResourceManagerUtil s_resourceManagerUtil; static ApiKeyPairDao s_apiKeyPairDao; static SnapshotPolicyDetailsDao s_snapshotPolicyDetailsDao; @@ -760,6 +763,8 @@ public class ApiDBUtils { @Inject private NicDao nicDao; @Inject + private NicDetailsDao nicDetailsDao; + @Inject private ResourceIconDao resourceIconDao; @Inject private ResourceManagerUtil resourceManagerUtil; @@ -890,6 +895,7 @@ void init() { s_clusterDetailsDao = clusterDetailsDao; s_vmSnapshotDao = vmSnapshotDao; s_nicDao = nicDao; + s_nicDetailsDao = nicDetailsDao; s_nicSecondaryIpDao = nicSecondaryIpDao; s_vpcProvSvc = vpcProvSvc; s_affinityGroupDao = affinityGroupDao; @@ -2232,6 +2238,10 @@ public static NicVO findNicById(long nicId) { return s_nicDao.findById(nicId); } + public static NicDetailVO findNicDetailByName(long nicId, String detailName) { + return s_nicDetailsDao.findDetail(nicId, detailName); + } + public static TemplateResponse newTemplateUpdateResponse(TemplateJoinVO vr) { return s_templateJoinDao.newUpdateResponse(vr); } diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index a8551b4c6693..4d8784d2dd26 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -454,6 +454,7 @@ import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; +import com.cloud.vm.NicDetailVO; import com.cloud.vm.dao.NicExtraDhcpOptionDao; import com.cloud.vm.dao.NicSecondaryIpVO; import com.cloud.vm.snapshot.VMSnapshot; @@ -2703,6 +2704,10 @@ public NetworkResponse createNetworkResponse(ResponseView view, Network network) response.setNetworkDomain(network.getNetworkDomain()); response.setPublicMtu(network.getPublicMtu()); response.setPrivateMtu(network.getPrivateMtu()); + NetworkDetailVO networkRateDetail = networkDetailsDao.findDetail(network.getId(), ApiConstants.NETWORKRATE); + if (networkRateDetail != null) { + response.setNetworkRate(networkRateDetail.getValue()); + } response.setDns1(profile.getDns1()); response.setDns2(profile.getDns2()); response.setIpv6Dns1(profile.getIp6Dns1()); @@ -4876,6 +4881,12 @@ public NicResponse createNicResponse(Nic result) { } response.setEnabled(result.isEnabled()); + + NicDetailVO nicRateDetail = ApiDBUtils.findNicDetailByName(result.getId(), ApiConstants.NETWORKRATE); + if (nicRateDetail != null) { + response.setNetworkRate(nicRateDetail.getValue()); + } + return response; } diff --git a/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java index 72690091e40e..bdb49ad53e79 100644 --- a/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java @@ -87,6 +87,7 @@ import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.net.Dhcp; +import com.cloud.vm.NicDetailVO; import com.cloud.vm.UserVmManager; import com.cloud.vm.VMInstanceDetailVO; import com.cloud.vm.VirtualMachine; @@ -404,6 +405,10 @@ public UserVmResponse newUserVmResponse(ResponseView view, String objectName, Us .collect(Collectors.toList()); nicResponse.setExtraDhcpOptions(nicExtraDhcpOptionResponses); + NicDetailVO nicNetworkRateDetail = ApiDBUtils.findNicDetailByName(userVm.getNicId(), ApiConstants.NETWORKRATE); + if (nicNetworkRateDetail != null) { + nicResponse.setNetworkRate(nicNetworkRateDetail.getValue()); + } userVmResponse.addNic(nicResponse); } } @@ -660,6 +665,11 @@ public UserVmResponse setUserVmResponse(ResponseView view, UserVmResponse userVm .map(vo -> new NicExtraDhcpOptionResponse(Dhcp.DhcpOptionCode.valueOfInt(vo.getCode()).getName(), vo.getCode(), vo.getValue())) .collect(Collectors.toList()); nicResponse.setExtraDhcpOptions(nicExtraDhcpOptionResponses); + + NicDetailVO nicNetworkRateDetail = ApiDBUtils.findNicDetailByName(uvo.getNicId(), ApiConstants.NETWORKRATE); + if (nicNetworkRateDetail != null) { + nicResponse.setNetworkRate(nicNetworkRateDetail.getValue()); + } userVmData.addNic(nicResponse); } diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java index d18fd043f697..935bd8e83002 100644 --- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java @@ -3604,6 +3604,9 @@ public void doInTransactionWithoutResult(TransactionStatus status) { UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), nicIdString, networkOfferingId, null, isDefault, VirtualMachine.class.getName(), vm.getUuid(), vm.isDisplay()); } + Integer rate = _configMgr.getNetworkOfferingNetworkRate(networkOfferingId, network.getDataCenterId()); + String networkRate = (rate == null || rate <= 0) ? ApiConstants.UNLIMITED : String.valueOf(rate); + _networkDetailsDao.addDetail(networkId, ApiConstants.NETWORKRATE, networkRate, true); } }); } else { diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index bb55f570927b..4695a05b8056 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -403,6 +403,7 @@ import com.cloud.vm.dao.InstanceGroupDao; import com.cloud.vm.dao.InstanceGroupVMMapDao; import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.NicDetailsDao; import com.cloud.vm.dao.NicExtraDhcpOptionDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; @@ -499,6 +500,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Inject private NicDao _nicDao; @Inject + private NicDetailsDao nicDetailsDao; + @Inject private RulesManager _rulesMgr; @Inject private LoadBalancingRulesManager _lbMgr; @@ -1532,6 +1535,7 @@ public UserVm addNicToVirtualMachine(AddNicToVMCmd cmd) throws InvalidParameterV saveExtraDhcpOptions(guestNic.getId(), cmd.getDhcpOptionsMap()); _networkMgr.configureExtraDhcpOptions(network, guestNic.getId(), cmd.getDhcpOptionsMap()); cleanUp = false; + saveNetworkRateInDetails(guestNic.getId(), guestNic.getNetworkRate()); } catch (ResourceUnavailableException e) { throw new CloudRuntimeException("Unable to add NIC to " + vmInstance + ": " + e); } catch (InsufficientCapacityException e) { @@ -1552,6 +1556,18 @@ public UserVm addNicToVirtualMachine(AddNicToVMCmd cmd) throws InvalidParameterV return _vmDao.findById(vmInstance.getId()); } + private void saveNetworkRateInDetails(long nicId, Integer rate) { + String networkRate = (rate == null || rate <= 0) ? ApiConstants.UNLIMITED : String.valueOf(rate); + nicDetailsDao.addDetail(nicId, ApiConstants.NETWORKRATE, networkRate, true); + } + + private void refreshNicNetworkRates(long vmId) { + List nics = _nicDao.listByVmId(vmId); + for (NicVO nic : nics) { + saveNetworkRateInDetails(nic.getId(), _networkModel.getNetworkRate(nic.getNetworkId(), vmId)); + } + } + /** * Set NIC as default if VM has no default NIC * @param vmInstance VM instance to be checked @@ -3466,7 +3482,10 @@ public UserVm startVirtualMachine(StartVMCmd cmd) throws ExecutionException, Con additonalParams.put(VirtualMachineProfile.Param.ConsiderLastHost, cmd.getConsiderLastHost().toString()); } - return startVirtualMachine(cmd.getId(), cmd.getPodId(), cmd.getClusterId(), cmd.getHostId(), additonalParams, cmd.getDeploymentPlanner()).first(); + UserVm vm = startVirtualMachine(cmd.getId(), cmd.getPodId(), cmd.getClusterId(), cmd.getHostId(), additonalParams, cmd.getDeploymentPlanner()).first(); + // Refresh nic_details with current network rates — the network offering may have changed since the VM was last running + refreshNicNetworkRates(vm.getId()); + return vm; } @Override diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js index 50c2ff4250b0..7fb0a2af76c0 100644 --- a/ui/src/config/section/network.js +++ b/ui/src/config/section/network.js @@ -49,7 +49,7 @@ export default { return fields }, details: () => { - const fields = ['name', 'id', 'description', 'type', 'traffictype', 'vpcid', 'vlan', 'cidr', 'ip6cidr', 'netmask', 'gateway', 'asnumber', 'aclname', 'ispersistent', 'restartrequired', 'reservediprange', 'redundantrouter', 'networkdomain', 'egressdefaultpolicy', 'zonename', 'account', 'domainpath', 'associatednetwork', 'associatednetworkid', 'ip4routing', 'ip6firewall', 'ip6routing', 'ip6routes', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu', 'privatemtu'] + const fields = ['name', 'id', 'description', 'type', 'traffictype', 'vpcid', 'vlan', 'cidr', 'ip6cidr', 'netmask', 'gateway', 'asnumber', 'aclname', 'ispersistent', 'restartrequired', 'reservediprange', 'redundantrouter', 'networkdomain', 'egressdefaultpolicy', 'zonename', 'account', 'domainpath', 'associatednetwork', 'associatednetworkid', 'ip4routing', 'ip6firewall', 'ip6routing', 'ip6routes', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu', 'privatemtu', 'networkrate'] if (isAdmin()) { const vlanIndex = fields.findIndex(detail => detail === 'vlan') fields.splice(vlanIndex + 1, 0, 'broadcasturi') diff --git a/ui/src/config/section/offering.js b/ui/src/config/section/offering.js index 9d7b743a70aa..ee9f5d8649c4 100644 --- a/ui/src/config/section/offering.js +++ b/ui/src/config/section/offering.js @@ -39,9 +39,9 @@ export default { return params }, filters: ['active', 'inactive'], - columns: ['name', 'displaytext', 'state', 'cpunumber', 'cpuspeed', 'memory', 'gpu', 'domain', 'zone', 'order'], + columns: ['name', 'displaytext', 'state', 'cpunumber', 'cpuspeed', 'memory', 'gpu', 'domain', 'zone', 'order', 'networkrate'], details: () => { - var fields = ['name', 'id', 'displaytext', 'offerha', 'provisioningtype', 'storagetype', 'iscustomized', 'iscustomizediops', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'hosttags', 'tags', 'storageaccessgroups', 'storagetags', 'domain', 'zone', 'created', 'dynamicscalingenabled', 'diskofferingstrictness', 'encryptroot', 'purgeresources', 'leaseduration', 'gpucardid', 'gpucardname', 'vgpuprofileid', 'vgpuprofilename', 'gpucount', 'gpudisplay', 'leaseexpiryaction', 'externaldetails'] + var fields = ['name', 'id', 'displaytext', 'offerha', 'provisioningtype', 'storagetype', 'iscustomized', 'iscustomizediops', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'hosttags', 'tags', 'storageaccessgroups', 'storagetags', 'domain', 'zone', 'created', 'dynamicscalingenabled', 'diskofferingstrictness', 'encryptroot', 'purgeresources', 'leaseduration', 'gpucardid', 'gpucardname', 'vgpuprofileid', 'vgpuprofilename', 'gpucount', 'gpudisplay', 'leaseexpiryaction', 'externaldetails', 'networkrate'] if (store.getters.apis.createServiceOffering && store.getters.apis.createServiceOffering.params.filter(x => x.name === 'storagepolicy').length > 0) { fields.splice(6, 0, 'vspherestoragepolicy') diff --git a/ui/src/views/network/NicsTable.vue b/ui/src/views/network/NicsTable.vue index 11ba135e39a2..8a036e7b9088 100644 --- a/ui/src/views/network/NicsTable.vue +++ b/ui/src/views/network/NicsTable.vue @@ -57,6 +57,9 @@ {{ record.isolationuri }} + + {{ record.networkrate }} + From adbdec2c511e9bab10c9fd79f42f6a8fda055cd8 Mon Sep 17 00:00:00 2001 From: Manoj Kumar Date: Fri, 4 Sep 2026 16:25:51 +0530 Subject: [PATCH 2/5] server,engine,api: persist and expose NIC network rate from nics column - Add network_rate column to nics table (schema-42300to42400.sql) - Add DB upgrade path: Upgrade42300to42400 registered in DatabaseUpgradeChecker - Add network_rate field and getter/setter to NicVO - Set network_rate on NicVO in NetworkOrchestrator.allocateNic() where rate is already computed, eliminating secondary per-NIC update calls - Add getNetworkRate() to Nic interface so ApiResponseHelper.createNicResponse can call result.getNetworkRate() without casting or extra DB queries - Add nic_network_rate to user_vm_view and UserVmJoinVO so listVirtualMachines reads rate from the join without extra per-NIC findNicById calls - Update UserVmJoinDaoImpl to use uvo.getNicNetworkRate() directly - Expose network_rate in NicResponse as Integer (null = unlimited) - Refresh NIC rates on VM start via refreshNicNetworkRates in UserVmManagerImpl --- api/src/main/java/com/cloud/vm/Nic.java | 2 + .../cloudstack/api/response/NicResponse.java | 8 +-- .../orchestration/NetworkOrchestrator.java | 3 +- .../cloud/upgrade/DatabaseUpgradeChecker.java | 2 + .../upgrade/dao/Upgrade42300to42400.java | 54 +++++++++++++++++++ .../src/main/java/com/cloud/vm/NicVO.java | 11 ++++ .../META-INF/db/schema-42300to42400.sql | 20 +++++++ .../META-INF/db/views/cloud.user_vm_view.sql | 1 + .../java/com/cloud/api/ApiResponseHelper.java | 6 +-- .../api/query/dao/UserVmJoinDaoImpl.java | 11 +--- .../com/cloud/api/query/vo/UserVmJoinVO.java | 7 +++ .../java/com/cloud/vm/UserVmManagerImpl.java | 13 ++--- 12 files changed, 109 insertions(+), 29 deletions(-) create mode 100644 engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42300to42400.java create mode 100644 engine/schema/src/main/resources/META-INF/db/schema-42300to42400.sql diff --git a/api/src/main/java/com/cloud/vm/Nic.java b/api/src/main/java/com/cloud/vm/Nic.java index 3722e5769c92..08ef5ea82469 100644 --- a/api/src/main/java/com/cloud/vm/Nic.java +++ b/api/src/main/java/com/cloud/vm/Nic.java @@ -168,5 +168,7 @@ public enum ReservationStrategy { Integer getMtu(); + Integer getNetworkRate(); + boolean isEnabled(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NicResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NicResponse.java index 6613c11ab87d..314d26b2ebfd 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NicResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NicResponse.java @@ -139,8 +139,8 @@ public class NicResponse extends BaseResponse { private Integer mtu; @SerializedName(ApiConstants.NETWORKRATE) - @Param(description = "Network rate (in Mb/s) configured for the NIC", since = "4.24.0") - private String networkRate; + @Param(description = "Network rate (in Mb/s) configured for the NIC; absent if unlimited", since = "4.24.0") + private Integer networkRate; @SerializedName(ApiConstants.PUBLIC_IP_ID) @Param(description = "Public IP address ID associated with this NIC via Static NAT rule") @@ -417,11 +417,11 @@ public void setMtu(Integer mtu) { this.mtu = mtu; } - public String getNetworkRate() { + public Integer getNetworkRate() { return networkRate; } - public void setNetworkRate(String networkRate) { + public void setNetworkRate(Integer networkRate) { this.networkRate = networkRate; } diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index 95f4f943aae1..982c8ab2097d 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -1234,14 +1234,15 @@ public Pair allocateNic(final NicProfile requested, final N NicVO vo = checkForRaceAndAllocateNic(requested, network, isDefaultNic, deviceId, vm); final Integer networkRate = _networkModel.getNetworkRate(network.getId(), vm.getId()); + vo.setNetworkRate(networkRate != null && networkRate > 0 ? networkRate : null); final NicProfile vmNic = new NicProfile(vo, network, vo.getBroadcastUri(), vo.getIsolationUri(), networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vm.getHypervisorType(), network)); if (vm.getType() == Type.DomainRouter) { Pair networks = getGuestNetworkRouterAndVpcDetails(vm.getId()); setMtuDetailsInVRNic(networks, network, vo); - _nicDao.update(vo.getId(), vo); setMtuInVRNicProfile(networks, network.getTrafficType(), vmNic); } + _nicDao.update(vo.getId(), vo); return new Pair<>(vmNic, Integer.valueOf(deviceId)); } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java index c3a982aa70e5..0e40d7b7401c 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java @@ -96,6 +96,7 @@ import com.cloud.upgrade.dao.Upgrade420to421; import com.cloud.upgrade.dao.Upgrade421to430; import com.cloud.upgrade.dao.Upgrade42210to42300; +import com.cloud.upgrade.dao.Upgrade42300to42400; import com.cloud.upgrade.dao.Upgrade430to440; import com.cloud.upgrade.dao.Upgrade431to440; import com.cloud.upgrade.dao.Upgrade432to440; @@ -248,6 +249,7 @@ public DatabaseUpgradeChecker() { .next("4.21.0.0", new Upgrade42100to42200()) .next("4.22.0.0", new Upgrade42200to42210()) .next("4.22.1.0", new Upgrade42210to42300()) + .next("4.23.0.0", new Upgrade42300to42400()) .build(); } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42300to42400.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42300to42400.java new file mode 100644 index 000000000000..ac617060eebd --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42300to42400.java @@ -0,0 +1,54 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.upgrade.dao; + +import java.io.InputStream; +import java.sql.Connection; + +import com.cloud.utils.exception.CloudRuntimeException; + +public class Upgrade42300to42400 extends DbUpgradeAbstractImpl implements DbUpgrade { + + @Override + public String[] getUpgradableVersionRange() { + return new String[]{"4.23.0.0", "4.24.0.0"}; + } + + @Override + public String getUpgradedVersion() { + return "4.24.0.0"; + } + + @Override + public InputStream[] getPrepareScripts() { + final String scriptFile = "META-INF/db/schema-42300to42400.sql"; + final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile); + if (script == null) { + throw new CloudRuntimeException("Unable to find " + scriptFile); + } + return new InputStream[]{script}; + } + + @Override + public void performDataMigration(Connection conn) { + } + + @Override + public InputStream[] getCleanupScripts() { + return null; + } +} diff --git a/engine/schema/src/main/java/com/cloud/vm/NicVO.java b/engine/schema/src/main/java/com/cloud/vm/NicVO.java index 65946b8d8210..dd405381cb9b 100644 --- a/engine/schema/src/main/java/com/cloud/vm/NicVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/NicVO.java @@ -131,6 +131,9 @@ protected NicVO() { @Column(name = "mtu") Integer mtu; + @Column(name = "network_rate") + Integer networkRate; + @Column(name = "enabled") boolean enabled; @@ -426,4 +429,12 @@ public Integer getMtu() { public void setMtu(Integer mtu) { this.mtu = mtu; } + + public Integer getNetworkRate() { + return networkRate; + } + + public void setNetworkRate(Integer networkRate) { + this.networkRate = networkRate; + } } diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42300to42400.sql b/engine/schema/src/main/resources/META-INF/db/schema-42300to42400.sql new file mode 100644 index 000000000000..ea0c8e62ca0f --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-42300to42400.sql @@ -0,0 +1,20 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +-- Schema upgrade from 4.23.0.0 to 4.24.0.0 + +ALTER TABLE `cloud`.`nics` ADD COLUMN `network_rate` int unsigned DEFAULT NULL COMMENT 'effective network rate in Mb/s for this NIC'; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql index fbf126608eae..5f6c31043596 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql @@ -144,6 +144,7 @@ SELECT `nics`.`broadcast_uri` AS `broadcast_uri`, `nics`.`isolation_uri` AS `isolation_uri`, `nics`.`enabled` AS `is_nic_enabled`, + `nics`.`network_rate` AS `nic_network_rate`, `nic_details`.`value` AS `nic_dns_name`, `vpc`.`id` AS `vpc_id`, `vpc`.`uuid` AS `vpc_uuid`, diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index f350ca262237..7977291d76d6 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -454,7 +454,6 @@ import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; -import com.cloud.vm.NicDetailVO; import com.cloud.vm.dao.NicExtraDhcpOptionDao; import com.cloud.vm.dao.NicSecondaryIpVO; import com.cloud.vm.snapshot.VMSnapshot; @@ -4910,10 +4909,7 @@ public NicResponse createNicResponse(Nic result) { response.setEnabled(result.isEnabled()); - NicDetailVO nicRateDetail = ApiDBUtils.findNicDetailByName(result.getId(), ApiConstants.NETWORKRATE); - if (nicRateDetail != null) { - response.setNetworkRate(nicRateDetail.getValue()); - } + response.setNetworkRate(result.getNetworkRate()); return response; } diff --git a/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java index d3887c1a7db8..2d5cde339616 100644 --- a/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java @@ -97,7 +97,6 @@ import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.net.Dhcp; -import com.cloud.vm.NicDetailVO; import com.cloud.vm.UserVmManager; import com.cloud.vm.VMInstanceDetailVO; import com.cloud.vm.VirtualMachine; @@ -448,10 +447,7 @@ public UserVmResponse newUserVmResponse(ResponseView view, String objectName, Us .collect(Collectors.toList()); nicResponse.setExtraDhcpOptions(nicExtraDhcpOptionResponses); - NicDetailVO nicNetworkRateDetail = ApiDBUtils.findNicDetailByName(userVm.getNicId(), ApiConstants.NETWORKRATE); - if (nicNetworkRateDetail != null) { - nicResponse.setNetworkRate(nicNetworkRateDetail.getValue()); - } + nicResponse.setNetworkRate(userVm.getNicNetworkRate()); userVmResponse.addNic(nicResponse); } } @@ -750,10 +746,7 @@ public UserVmResponse setUserVmResponse(ResponseView view, UserVmResponse userVm .collect(Collectors.toList()); nicResponse.setExtraDhcpOptions(nicExtraDhcpOptionResponses); - NicDetailVO nicNetworkRateDetail = ApiDBUtils.findNicDetailByName(uvo.getNicId(), ApiConstants.NETWORKRATE); - if (nicNetworkRateDetail != null) { - nicResponse.setNetworkRate(nicNetworkRateDetail.getValue()); - } + nicResponse.setNetworkRate(uvo.getNicNetworkRate()); userVmData.addNic(nicResponse); } diff --git a/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java index 6f84bd9b1e3e..5e63e4383e09 100644 --- a/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java +++ b/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java @@ -355,6 +355,9 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro @Column(name = "is_nic_enabled") private boolean isNicEnabled; + @Column(name = "nic_network_rate") + private Integer nicNetworkRate; + @Column(name = "ip_address") private String ipAddress; @@ -1115,6 +1118,10 @@ public boolean isNicEnabled() { return isNicEnabled; } + public Integer getNicNetworkRate() { + return nicNetworkRate; + } + public String getNicDnsName() { return nicDnsName; } diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 23dfb12b24c6..5f814cd192b9 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -421,7 +421,6 @@ import com.cloud.vm.dao.InstanceGroupDao; import com.cloud.vm.dao.InstanceGroupVMMapDao; import com.cloud.vm.dao.NicDao; -import com.cloud.vm.dao.NicDetailsDao; import com.cloud.vm.dao.NicExtraDhcpOptionDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; @@ -525,8 +524,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Inject private NicDao _nicDao; @Inject - private NicDetailsDao nicDetailsDao; - @Inject private RulesManager _rulesMgr; @Inject private LoadBalancingRulesManager _lbMgr; @@ -1576,7 +1573,6 @@ public UserVm addNicToVirtualMachine(AddNicToVMCmd cmd) throws InvalidParameterV saveExtraDhcpOptions(guestNic.getId(), cmd.getDhcpOptionsMap()); _networkMgr.configureExtraDhcpOptions(network, guestNic.getId(), cmd.getDhcpOptionsMap()); cleanUp = false; - saveNetworkRateInDetails(guestNic.getId(), guestNic.getNetworkRate()); } catch (ResourceUnavailableException e) { throw new CloudRuntimeException("Unable to add NIC to " + vmInstance + ": " + e); } catch (InsufficientCapacityException e) { @@ -1598,15 +1594,12 @@ public UserVm addNicToVirtualMachine(AddNicToVMCmd cmd) throws InvalidParameterV return _vmDao.findById(vmInstance.getId()); } - private void saveNetworkRateInDetails(long nicId, Integer rate) { - String networkRate = (rate == null || rate <= 0) ? ApiConstants.UNLIMITED : String.valueOf(rate); - nicDetailsDao.addDetail(nicId, ApiConstants.NETWORKRATE, networkRate, true); - } - private void refreshNicNetworkRates(long vmId) { List nics = _nicDao.listByVmId(vmId); for (NicVO nic : nics) { - saveNetworkRateInDetails(nic.getId(), _networkModel.getNetworkRate(nic.getNetworkId(), vmId)); + Integer rate = _networkModel.getNetworkRate(nic.getNetworkId(), vmId); + nic.setNetworkRate(rate != null && rate > 0 ? rate : null); + _nicDao.update(nic.getId(), nic); } } From b684b88ab249ee2d6c2179038b9196d210ebe6ab Mon Sep 17 00:00:00 2001 From: Manoj Kumar Date: Mon, 7 Sep 2026 16:44:41 +0530 Subject: [PATCH 3/5] fix ui build due to merge conflict --- ui/src/config/section/offering.js | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/src/config/section/offering.js b/ui/src/config/section/offering.js index ee3de0486f14..6180d1467d2b 100644 --- a/ui/src/config/section/offering.js +++ b/ui/src/config/section/offering.js @@ -43,7 +43,6 @@ export default { } return [] }, - filters: ['active', 'inactive'], columns: ['name', 'displaytext', 'state', 'cpunumber', 'cpuspeed', 'memory', 'gpu', 'domain', 'zone', 'order', 'networkrate'], details: () => { var fields = ['name', 'id', 'displaytext', 'offerha', 'provisioningtype', 'storagetype', 'iscustomized', 'iscustomizediops', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'hosttags', 'tags', 'storageaccessgroups', 'storagetags', 'domain', 'zone', 'created', 'dynamicscalingenabled', 'diskofferingstrictness', 'encryptroot', 'purgeresources', 'leaseduration', 'gpucardid', 'gpucardname', 'vgpuprofileid', 'vgpuprofilename', 'gpucount', 'gpudisplay', 'leaseexpiryaction', 'externaldetails', 'networkrate'] From 0a2b534411d23a9a020df6d3530c48f5af998963 Mon Sep 17 00:00:00 2001 From: Manoj Kumar Date: Tue, 15 Sep 2026 10:35:37 +0530 Subject: [PATCH 4/5] Fix network rate response inconsistency between Network, Nic and NetworkOffering APIs Align networkrate field type and unlimited semantics (-1) across NetworkResponse and NicResponse with the existing NetworkOfferingResponse convention, instead of using a String unlimited value or omitting the field entirely. --- .../main/java/org/apache/cloudstack/api/ApiConstants.java | 1 - .../apache/cloudstack/api/response/NetworkResponse.java | 8 ++++---- .../org/apache/cloudstack/api/response/NicResponse.java | 2 +- .../engine/orchestration/NetworkOrchestrator.java | 3 +-- server/src/main/java/com/cloud/api/ApiResponseHelper.java | 7 +++++-- .../java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java | 6 ++++-- .../main/java/com/cloud/network/NetworkServiceImpl.java | 3 +-- 7 files changed, 16 insertions(+), 14 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 507049b660f7..f74c46161180 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -1216,7 +1216,6 @@ public class ApiConstants { public static final String NETSCALER_CONTROLCENTER_ID = "netscalercontrolcenterid"; public static final String NETSCALER_SERVICEPACKAGE_ID = "netscalerservicepackageid"; public static final String FETCH_ROUTER_HEALTH_CHECK_RESULTS = "fetchhealthcheckresults"; - public static final String UNLIMITED = "unlimited"; public static final String ZONE_ID_LIST = "zoneids"; public static final String DESTINATION_ZONE_ID_LIST = "destzoneids"; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java index eee40983cea4..f1f9f6d18dea 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java @@ -312,8 +312,8 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement private Integer privateMtu; @SerializedName(ApiConstants.NETWORKRATE) - @Param(description = "Network rate (in Mb/s) configured for the Guest interface of this network", since = "4.24.0") - private String networkRate; + @Param(description = "Network rate (in Mb/s) configured for the Guest interface of this network; -1 if unlimited", since = "4.24.0") + private Integer networkRate; @SerializedName(ApiConstants.IP6_DNS1) @Param(description = "The first IPv6 DNS for the network", since = "4.18.0") @@ -711,11 +711,11 @@ public void setPrivateMtu(Integer privateMtu) { this.privateMtu = privateMtu; } - public String getNetworkRate() { + public Integer getNetworkRate() { return networkRate; } - public void setNetworkRate(String networkRate) { + public void setNetworkRate(Integer networkRate) { this.networkRate = networkRate; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NicResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NicResponse.java index 314d26b2ebfd..843c21f5fd34 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NicResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NicResponse.java @@ -139,7 +139,7 @@ public class NicResponse extends BaseResponse { private Integer mtu; @SerializedName(ApiConstants.NETWORKRATE) - @Param(description = "Network rate (in Mb/s) configured for the NIC; absent if unlimited", since = "4.24.0") + @Param(description = "Network rate (in Mb/s) configured for the NIC; -1 if unlimited", since = "4.24.0") private Integer networkRate; @SerializedName(ApiConstants.PUBLIC_IP_ID) diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index 982c8ab2097d..77eebae4e2a5 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -779,8 +779,7 @@ private void updateRouterIpInNetworkDetails(Long networkId, String routerIp, Str private void saveNetworkRateInDetails(long networkId, NetworkOffering offering, long dataCenterId) { Integer rate = _configMgr.getNetworkOfferingNetworkRate(offering.getId(), dataCenterId); - String networkRate = (rate == null || rate <= 0) ? ApiConstants.UNLIMITED : String.valueOf(rate); - networkDetailsDao.addDetail(networkId, ApiConstants.NETWORKRATE, networkRate, true); + networkDetailsDao.addDetail(networkId, ApiConstants.NETWORKRATE, String.valueOf(rate), true); } @Override diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 5564ab370695..c16e269ef801 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -243,6 +243,7 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -2717,7 +2718,8 @@ public NetworkResponse createNetworkResponse(ResponseView view, Network network) response.setPrivateMtu(network.getPrivateMtu()); NetworkDetailVO networkRateDetail = networkDetailsDao.findDetail(network.getId(), ApiConstants.NETWORKRATE); if (networkRateDetail != null) { - response.setNetworkRate(networkRateDetail.getValue()); + int networkRate = NumberUtils.toInt(networkRateDetail.getValue(), -1); + response.setNetworkRate(networkRate > 0 ? networkRate : -1); } response.setDns1(profile.getDns1()); response.setDns2(profile.getDns2()); @@ -4910,7 +4912,8 @@ public NicResponse createNicResponse(Nic result) { response.setEnabled(result.isEnabled()); - response.setNetworkRate(result.getNetworkRate()); + Integer nicNetworkRate = result.getNetworkRate(); + response.setNetworkRate(nicNetworkRate != null && nicNetworkRate > 0 ? nicNetworkRate : -1); return response; } diff --git a/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java index 2d5cde339616..67ab53b61132 100644 --- a/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java @@ -447,7 +447,8 @@ public UserVmResponse newUserVmResponse(ResponseView view, String objectName, Us .collect(Collectors.toList()); nicResponse.setExtraDhcpOptions(nicExtraDhcpOptionResponses); - nicResponse.setNetworkRate(userVm.getNicNetworkRate()); + Integer nicNetworkRate = userVm.getNicNetworkRate(); + nicResponse.setNetworkRate(nicNetworkRate != null && nicNetworkRate > 0 ? nicNetworkRate : -1); userVmResponse.addNic(nicResponse); } } @@ -746,7 +747,8 @@ public UserVmResponse setUserVmResponse(ResponseView view, UserVmResponse userVm .collect(Collectors.toList()); nicResponse.setExtraDhcpOptions(nicExtraDhcpOptionResponses); - nicResponse.setNetworkRate(uvo.getNicNetworkRate()); + Integer nicNetworkRate = uvo.getNicNetworkRate(); + nicResponse.setNetworkRate(nicNetworkRate != null && nicNetworkRate > 0 ? nicNetworkRate : -1); userVmData.addNic(nicResponse); } diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java index 6c8a8f4e4a0e..dd43cf16e0b3 100644 --- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java @@ -3605,8 +3605,7 @@ public void doInTransactionWithoutResult(TransactionStatus status) { null, isDefault, VirtualMachine.class.getName(), vm.getUuid(), vm.isDisplay()); } Integer rate = _configMgr.getNetworkOfferingNetworkRate(networkOfferingId, network.getDataCenterId()); - String networkRate = (rate == null || rate <= 0) ? ApiConstants.UNLIMITED : String.valueOf(rate); - _networkDetailsDao.addDetail(networkId, ApiConstants.NETWORKRATE, networkRate, true); + _networkDetailsDao.addDetail(networkId, ApiConstants.NETWORKRATE, String.valueOf(rate), true); } }); } else { From 71d49b86f1ec24e55f0ec38c079a21628dc58a55 Mon Sep 17 00:00:00 2001 From: Manoj Kumar Date: Wed, 16 Sep 2026 09:43:36 +0530 Subject: [PATCH 5/5] Apply new precedence rules for VM and VR NIC network rate Instance NICs now derive rate solely from the VM's own offering. VR guest interfaces check the router's own system offering first, then fall back to the network offering. Persist rate once in prepareNic() for all VM types. Also surface NIC network rate on listRouters. Existing NICs won't reflect the new precedence until their VM/router is restarted; a DB backfill for pre-existing NICs is tracked as a separate follow-up. --- .../orchestration/NetworkOrchestrator.java | 2 ++ .../db/views/cloud.domain_router_view.sql | 1 + .../src/main/java/com/cloud/api/ApiDBUtils.java | 10 ---------- .../api/query/dao/DomainRouterJoinDaoImpl.java | 4 ++++ .../cloud/api/query/vo/DomainRouterJoinVO.java | 7 +++++++ .../com/cloud/network/NetworkModelImpl.java | 17 ++++++++++++++--- .../java/com/cloud/vm/UserVmManagerImpl.java | 14 +------------- 7 files changed, 29 insertions(+), 26 deletions(-) diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index 77eebae4e2a5..ae74c8f49027 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -2307,6 +2307,8 @@ public NicProfile prepareNic(final VirtualMachineProfile vmProfile, final Deploy nic.setState(Nic.State.Reserved); } + nic.setNetworkRate(networkRate != null && networkRate > 0 ? networkRate : null); + if (vmProfile.getType() == Type.DomainRouter) { Pair networks = getGuestNetworkRouterAndVpcDetails(vmProfile.getId()); setMtuDetailsInVRNic(networks, network, nic); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_router_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_router_view.sql index d5f17606cb41..524048b9198f 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_router_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_router_view.sql @@ -77,6 +77,7 @@ select nics.isolation_uri isolation_uri, nics.mtu mtu, nics.enabled is_nic_enabled, + nics.network_rate nic_network_rate, vpc.id vpc_id, vpc.uuid vpc_uuid, vpc.name vpc_name, diff --git a/server/src/main/java/com/cloud/api/ApiDBUtils.java b/server/src/main/java/com/cloud/api/ApiDBUtils.java index 4067e5ab0dcb..934600eb2b61 100644 --- a/server/src/main/java/com/cloud/api/ApiDBUtils.java +++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java @@ -343,7 +343,6 @@ import com.cloud.vm.DomainRouterVO; import com.cloud.vm.InstanceGroup; import com.cloud.vm.InstanceGroupVO; -import com.cloud.vm.NicDetailVO; import com.cloud.vm.NicProfile; import com.cloud.vm.NicVO; import com.cloud.vm.VMInstanceDetailVO; @@ -357,7 +356,6 @@ import com.cloud.vm.dao.ConsoleProxyDao; import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.NicDao; -import com.cloud.vm.dao.NicDetailsDao; import com.cloud.vm.dao.NicSecondaryIpDao; import com.cloud.vm.dao.NicSecondaryIpVO; import com.cloud.vm.dao.UserVmDao; @@ -504,7 +502,6 @@ public class ApiDBUtils { static BackupOfferingDao s_backupOfferingDao; static BackupRepositoryDao s_backupRepositoryDao; static NicDao s_nicDao; - static NicDetailsDao s_nicDetailsDao; static ResourceManagerUtil s_resourceManagerUtil; static ApiKeyPairDao s_apiKeyPairDao; static SnapshotPolicyDetailsDao s_snapshotPolicyDetailsDao; @@ -771,8 +768,6 @@ public class ApiDBUtils { @Inject private NicDao nicDao; @Inject - private NicDetailsDao nicDetailsDao; - @Inject private ResourceIconDao resourceIconDao; @Inject private ResourceManagerUtil resourceManagerUtil; @@ -907,7 +902,6 @@ void init() { s_clusterDetailsDao = clusterDetailsDao; s_vmSnapshotDao = vmSnapshotDao; s_nicDao = nicDao; - s_nicDetailsDao = nicDetailsDao; s_nicSecondaryIpDao = nicSecondaryIpDao; s_vpcProvSvc = vpcProvSvc; s_affinityGroupDao = affinityGroupDao; @@ -2256,10 +2250,6 @@ public static NicVO findNicById(long nicId) { return s_nicDao.findById(nicId); } - public static NicDetailVO findNicDetailByName(long nicId, String detailName) { - return s_nicDetailsDao.findDetail(nicId, detailName); - } - public static TemplateResponse newTemplateUpdateResponse(TemplateJoinVO vr) { return s_templateJoinDao.newUpdateResponse(vr); } diff --git a/server/src/main/java/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java index 9bc409d455e7..d4ae4f1db240 100644 --- a/server/src/main/java/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java @@ -198,6 +198,8 @@ public DomainRouterResponse newDomainRouterResponse(DomainRouterJoinVO router, A nicResponse.setIsDefault(router.isDefaultNic()); nicResponse.setEnabled(router.isNicEnabled()); nicResponse.setObjectName("nic"); + Integer routerNicNetworkRate = router.getNicNetworkRate(); + nicResponse.setNetworkRate(routerNicNetworkRate != null && routerNicNetworkRate > 0 ? routerNicNetworkRate : -1); routerResponse.addNic(nicResponse); } } @@ -292,6 +294,8 @@ public DomainRouterResponse setDomainRouterResponse(DomainRouterResponse vrData, nicResponse.setIsDefault(vr.isDefaultNic()); nicResponse.setEnabled(vr.isNicEnabled()); nicResponse.setObjectName("nic"); + Integer vrNicNetworkRate = vr.getNicNetworkRate(); + nicResponse.setNetworkRate(vrNicNetworkRate != null && vrNicNetworkRate > 0 ? vrNicNetworkRate : -1); vrData.addNic(nicResponse); } return vrData; diff --git a/server/src/main/java/com/cloud/api/query/vo/DomainRouterJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/DomainRouterJoinVO.java index db7f75b6f2bb..7cdeb370a2a9 100644 --- a/server/src/main/java/com/cloud/api/query/vo/DomainRouterJoinVO.java +++ b/server/src/main/java/com/cloud/api/query/vo/DomainRouterJoinVO.java @@ -210,6 +210,9 @@ public class DomainRouterJoinVO extends BaseViewVO implements ControlledViewEnti @Column(name = "isolation_uri") private URI isolationUri; + @Column(name = "nic_network_rate") + private Integer nicNetworkRate; + @Column(name = "network_id") private long networkId; @@ -450,6 +453,10 @@ public URI getIsolationUri() { return isolationUri; } + public Integer getNicNetworkRate() { + return nicNetworkRate; + } + public long getNetworkId() { return networkId; } diff --git a/server/src/main/java/com/cloud/network/NetworkModelImpl.java b/server/src/main/java/com/cloud/network/NetworkModelImpl.java index f47046cdc434..d0456e0e9de3 100644 --- a/server/src/main/java/com/cloud/network/NetworkModelImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkModelImpl.java @@ -123,6 +123,8 @@ import com.cloud.projects.ProjectAccount; import com.cloud.projects.dao.ProjectAccountDao; import com.cloud.projects.dao.ProjectDao; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.AccountVO; @@ -173,6 +175,8 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi @Inject ConfigurationManager _configMgr; @Inject + ServiceOfferingDao _serviceOfferingDao; + @Inject NetworkOfferingDao _networkOfferingDao = null; @Inject NetworkDao _networksDao = null; @@ -1202,9 +1206,11 @@ public Integer getNetworkRate(long networkId, Long vmId) { final Network network = getNetwork(networkId); final NetworkOffering ntwkOff = _entityMgr.findById(NetworkOffering.class, network.getNetworkOfferingId()); - // For user VM: For default nic use network rate from the service/compute offering, + // For user VM: Use network rate from the service/compute offering for every nic (default or not), // or on NULL from vm.network.throttling.rate global setting - // For router: Get network rate for guest and public networks from the guest network offering + // For router: For guest networks, use network rate from the router's own system offering first, + // falling back to the guest network offering, or on NULL from network.throttling.rate + // For public networks, use network rate from the router's guest network offering, // or on NULL from network.throttling.rate // For others: Use network rate from their network offering, // or on NULL from network.throttling.rate setting at zone > global level @@ -1213,7 +1219,7 @@ public Integer getNetworkRate(long networkId, Long vmId) { switch (vm.getType()) { case User: final Nic nic = _nicDao.findByNtwkIdAndInstanceId(networkId, vmId); - if (nic != null && nic.isDefaultNic()) { + if (nic != null) { return _configMgr.getServiceOfferingNetworkRate(vm.getServiceOfferingId(), network.getDataCenterId()); } break; @@ -1221,6 +1227,11 @@ public Integer getNetworkRate(long networkId, Long vmId) { if (TrafficType.Guest.equals(network.getTrafficType())) { final Nic routerNic = _nicDao.findByNtwkIdAndInstanceId(networkId, vmId); if (routerNic != null) { + final ServiceOfferingVO routerOffering = _serviceOfferingDao.findById(vm.getServiceOfferingId()); + if (routerOffering != null && routerOffering.getRateMbps() != null) { + final int systemOfferingRate = routerOffering.getRateMbps(); + return systemOfferingRate > 0 ? systemOfferingRate : -1; + } return _configMgr.getNetworkOfferingNetworkRate(network.getNetworkOfferingId(), network.getDataCenterId()); } } else if (TrafficType.Public.equals(network.getTrafficType())) { diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 053a809ff5c0..60fdee8feaa7 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -1594,15 +1594,6 @@ public UserVm addNicToVirtualMachine(AddNicToVMCmd cmd) throws InvalidParameterV return _vmDao.findById(vmInstance.getId()); } - private void refreshNicNetworkRates(long vmId) { - List nics = _nicDao.listByVmId(vmId); - for (NicVO nic : nics) { - Integer rate = _networkModel.getNetworkRate(nic.getNetworkId(), vmId); - nic.setNetworkRate(rate != null && rate > 0 ? rate : null); - _nicDao.update(nic.getId(), nic); - } - } - private void validateVmZoneTypeForAddNic(UserVmVO vmInstance, DataCenter dc) { if (!NetworkType.Basic.equals(dc.getNetworkType())) { return; @@ -3587,10 +3578,7 @@ public UserVm startVirtualMachine(StartVMCmd cmd) throws ExecutionException, Con additonalParams.put(VirtualMachineProfile.Param.ConsiderLastHost, cmd.getConsiderLastHost().toString()); } - UserVm vm = startVirtualMachine(cmd.getId(), cmd.getPodId(), cmd.getClusterId(), cmd.getHostId(), additonalParams, cmd.getDeploymentPlanner(), false).first(); - // Refresh nic_details with current network rates — the network offering may have changed since the VM was last running - refreshNicNetworkRates(vm.getId()); - return vm; + return startVirtualMachine(cmd.getId(), cmd.getPodId(), cmd.getClusterId(), cmd.getHostId(), additonalParams, cmd.getDeploymentPlanner(), false).first(); } @Override