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 f74c46161180..507049b660f7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -1216,6 +1216,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 aeaf333540c7..eee40983cea4 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; @@ -707,6 +711,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 5ad41ad62244..6613c11ab87d 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; @@ -413,6 +417,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 8af75562b31c..95f4f943aae1 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 @@ -777,6 +777,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 { @@ -852,6 +858,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 934600eb2b61..4067e5ab0dcb 100644 --- a/server/src/main/java/com/cloud/api/ApiDBUtils.java +++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java @@ -343,6 +343,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; @@ -356,6 +357,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; @@ -502,6 +504,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; @@ -768,6 +771,8 @@ public class ApiDBUtils { @Inject private NicDao nicDao; @Inject + private NicDetailsDao nicDetailsDao; + @Inject private ResourceIconDao resourceIconDao; @Inject private ResourceManagerUtil resourceManagerUtil; @@ -902,6 +907,7 @@ void init() { s_clusterDetailsDao = clusterDetailsDao; s_vmSnapshotDao = vmSnapshotDao; s_nicDao = nicDao; + s_nicDetailsDao = nicDetailsDao; s_nicSecondaryIpDao = nicSecondaryIpDao; s_vpcProvSvc = vpcProvSvc; s_affinityGroupDao = affinityGroupDao; @@ -2250,6 +2256,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 2510dc0a88b4..f350ca262237 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; @@ -2714,6 +2715,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()); @@ -4904,6 +4909,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 ebc2326a72d5..d3887c1a7db8 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,6 +97,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; @@ -447,6 +448,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); } } @@ -744,6 +749,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 2853fa96330d..6c8a8f4e4a0e 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 070c2ae54783..23dfb12b24c6 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -421,6 +421,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; @@ -524,6 +525,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; @@ -1573,6 +1576,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) { @@ -1594,6 +1598,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)); + } + } + private void validateVmZoneTypeForAddNic(UserVmVO vmInstance, DataCenter dc) { if (!NetworkType.Basic.equals(dc.getNetworkType())) { return; @@ -3578,7 +3594,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(), false).first(); + 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; } @Override diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js index 3be69ab10614..ebf3280f1a3e 100644 --- a/ui/src/config/section/network.js +++ b/ui/src/config/section/network.js @@ -53,7 +53,7 @@ export default { 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', 'dnszone', 'dnssubdomain'] + 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu', 'privatemtu', 'networkrate', 'dnszone', 'dnssubdomain'] 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 afdf2605a6a8..3d4c08cc44fc 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 3a70eb3429c3..d6505539e8c0 100644 --- a/ui/src/views/network/NicsTable.vue +++ b/ui/src/views/network/NicsTable.vue @@ -57,6 +57,9 @@ {{ record.isolationuri }} + + {{ record.networkrate }} + {{ record.nicdnsname }}