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..9b103173a7b0 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 @@ -1665,6 +1681,7 @@ public UserVm removeNicFromVirtualMachine(RemoveNicFromVMCmd cmd) throws Invalid throw new CloudRuntimeException("Unable to remove " + network + " from " + vmInstance); } + nicDetailsDao.removeDetails(nicId); logger.debug("Successful removal of " + network + " from " + vmInstance); return _vmDao.findById(vmInstance.getId()); } @@ -3466,7 +3483,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..452218097d4f 100644 --- a/ui/src/views/network/NicsTable.vue +++ b/ui/src/views/network/NicsTable.vue @@ -57,6 +57,9 @@ {{ record.isolationuri }} + + {{ record.networkrate }} +