This is an automated email from the ASF dual-hosted git repository.
harikrishna-patnala pushed a commit to branch 4.22
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.22 by this push:
new 434df401b08 Routed: get vm network statistics on Routed network
(#13105)
434df401b08 is described below
commit 434df401b0878b1d9eeac4eafcafcba4609081a5
Author: Wei Zhou <[email protected]>
AuthorDate: Thu Sep 10 11:01:24 2026 +0200
Routed: get vm network statistics on Routed network (#13105)
* Routed: get vm network statistics on Routed network
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI
<[email protected]>
* Create method isNetworkEligibleForNetworkStats and add unit tests
---------
Co-authored-by: Copilot Autofix powered by AI
<[email protected]>
---
.../main/java/com/cloud/server/StatsCollector.java | 39 ++++++++--
.../java/com/cloud/server/StatsCollectorTest.java | 88 ++++++++++++++++++++++
2 files changed, 122 insertions(+), 5 deletions(-)
diff --git a/server/src/main/java/com/cloud/server/StatsCollector.java
b/server/src/main/java/com/cloud/server/StatsCollector.java
index 917e05a3c98..3eec27d4f54 100644
--- a/server/src/main/java/com/cloud/server/StatsCollector.java
+++ b/server/src/main/java/com/cloud/server/StatsCollector.java
@@ -57,6 +57,7 @@ import org.apache.cloudstack.framework.config.Configurable;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import org.apache.cloudstack.management.ManagementServerHost;
+import org.apache.cloudstack.network.RoutedIpv4Manager;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
@@ -113,6 +114,8 @@ import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.network.as.AutoScaleManager;
+import com.cloud.network.dao.NetworkDao;
+import com.cloud.network.dao.NetworkVO;
import com.cloud.org.Cluster;
import com.cloud.resource.ResourceManager;
import com.cloud.resource.ResourceState;
@@ -203,6 +206,11 @@ import com.sun.management.OperatingSystemMXBean;
@Component
public class StatsCollector extends ManagerBase implements
ComponentMethodInterceptable, Configurable, DbStatsCollection {
+ @Inject
+ private NetworkDao networkDao;
+ @Inject
+ private RoutedIpv4Manager routedIpv4Manager;
+
public static enum ExternalStatsProtocol {
NONE("none"), GRAPHITE("graphite"), INFLUXDB("influxdb");
String _type;
@@ -268,9 +276,9 @@ public class StatsCollector extends ManagerBase implements
ComponentMethodInterc
private static final ConfigKey<Integer> vmDiskStatsIntervalMin = new
ConfigKey<>("Advanced", Integer.class, "vm.disk.stats.interval.min", "300",
"Minimal interval (in seconds) to report vm disk statistics. If
vm.disk.stats.interval is smaller than this, use this to report vm disk
statistics.", false);
private static final ConfigKey<Integer> vmNetworkStatsInterval = new
ConfigKey<>("Advanced", Integer.class, "vm.network.stats.interval", "0",
- "Interval (in seconds) to report vm network statistics (for Shared
networks). Vm network statistics will be disabled if this is set to 0 or less
than 0.", false);
+ "Interval (in seconds) to report vm network statistics (for Shared
and Routed networks). Vm network statistics will be disabled if this is set to
0 or less than 0.", false);
private static final ConfigKey<Integer> vmNetworkStatsIntervalMin = new
ConfigKey<>("Advanced", Integer.class, "vm.network.stats.interval.min", "300",
- "Minimal Interval (in seconds) to report vm network statistics
(for Shared networks). If vm.network.stats.interval is smaller than this, use
this to report vm network statistics.",
+ "Minimal Interval (in seconds) to report vm network statistics
(for Shared and Routed networks). If vm.network.stats.interval is smaller than
this, use this to report vm network statistics.",
false);
private static final ConfigKey<Integer> StatsTimeout = new
ConfigKey<>("Advanced", Integer.class, "stats.timeout", "60000",
"The timeout for stats call in milli seconds.", true,
@@ -1601,9 +1609,9 @@ public class StatsCollector extends ManagerBase
implements ComponentMethodInterc
SearchCriteria<NicVO> sc_nic =
_nicDao.createSearchCriteria();
sc_nic.addAnd("macAddress",
SearchCriteria.Op.EQ, vmNetworkStatEntry.getMacAddress());
NicVO nic = _nicDao.search(sc_nic,
null).get(0);
- List<VlanVO> vlan =
_vlanDao.listVlansByNetworkId(nic.getNetworkId());
- if (vlan == null || vlan.size() == 0 ||
vlan.get(0).getVlanType() != VlanType.DirectAttached)
- continue; // only get network
statistics for DirectAttached network (shared networks in Basic zone and
Advanced zone with/without SG)
+ if
(!isNetworkEligibleForNetworkStats(nic.getNetworkId())) {
+ continue; // only get network
statistics for Shared or Routed network
+ }
UserStatisticsVO previousvmNetworkStats =
_userStatsDao.findBy(userVm.getAccountId(), userVm.getDataCenterId(),
nic.getNetworkId(),
nic.getIPv4Address(), vmId,
"UserVm");
if (previousvmNetworkStats == null) {
@@ -2159,6 +2167,27 @@ public class StatsCollector extends ManagerBase
implements ComponentMethodInterc
return true;
}
+ /**
+ * Returns {@code true} if the given network is eligible for VM network
statistics collection.
+ * Only Shared (DirectAttached) networks and Routed networks qualify.
+ *
+ * @param networkId the network id to evaluate
+ * @return {@code true} when the network is routed or direct-attached,
{@code false} otherwise
+ */
+ protected boolean isNetworkEligibleForNetworkStats(Long networkId) {
+ if (networkId == null) {
+ return false;
+ }
+ List<VlanVO> vlans = _vlanDao.listVlansByNetworkId(networkId);
+ boolean isDirectAttachedNetwork = CollectionUtils.isNotEmpty(vlans)
+ && vlans.get(0).getVlanType() == VlanType.DirectAttached;
+ if (isDirectAttachedNetwork) {
+ return true;
+ }
+ NetworkVO networkVO = networkDao.findById(networkId);
+ return networkVO != null &&
routedIpv4Manager.isRoutedNetwork(networkVO);
+ }
+
/**
* Returns true if all the VmDiskStatsEntry are Zeros (Bytes read, Bytes
write, IO read, and IO write must be all equals to zero)
*/
diff --git a/server/src/test/java/com/cloud/server/StatsCollectorTest.java
b/server/src/test/java/com/cloud/server/StatsCollectorTest.java
index 46449a9cb6e..cb00d1652c9 100644
--- a/server/src/test/java/com/cloud/server/StatsCollectorTest.java
+++ b/server/src/test/java/com/cloud/server/StatsCollectorTest.java
@@ -26,6 +26,7 @@ import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
@@ -38,6 +39,7 @@ import java.util.concurrent.TimeUnit;
import com.cloud.utils.DateUtil;
import com.google.gson.JsonSyntaxException;
import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.network.RoutedIpv4Manager;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.commons.collections.CollectionUtils;
import org.influxdb.InfluxDB;
@@ -64,7 +66,12 @@ import com.cloud.agent.api.GetStorageStatsAnswer;
import com.cloud.agent.api.GetStorageStatsCommand;
import com.cloud.agent.api.VmDiskStatsEntry;
import com.cloud.agent.api.VmStatsEntry;
+import com.cloud.dc.Vlan.VlanType;
+import com.cloud.dc.VlanVO;
+import com.cloud.dc.dao.VlanDao;
import com.cloud.hypervisor.Hypervisor;
+import com.cloud.network.dao.NetworkDao;
+import com.cloud.network.dao.NetworkVO;
import com.cloud.server.StatsCollector.ExternalStatsProtocol;
import com.cloud.storage.StorageStats;
import com.cloud.storage.VolumeStatsVO;
@@ -118,6 +125,15 @@ public class StatsCollectorTest {
@Mock
private StoragePoolVO mockPool;
+ @Mock
+ private RoutedIpv4Manager routedIpv4Manager;
+
+ @Mock
+ private NetworkDao networkDao;
+
+ @Mock
+ private VlanDao vlanDao;
+
private static Gson gson = new Gson();
private Gson msStatsGson;
@@ -744,6 +760,78 @@ public class StatsCollectorTest {
*/
}
+ // -----------------------------------------------------------------------
+ // Tests for isNetworkEligibleForNetworkStats
+ // -----------------------------------------------------------------------
+
+ private VlanVO buildVlan(VlanType type) {
+ VlanVO vlan = Mockito.mock(VlanVO.class);
+ Mockito.when(vlan.getVlanType()).thenReturn(type);
+ return vlan;
+ }
+
+ @Test
+ public void isNetworkEligibleForNetworkStats_RoutedNetwork_ReturnsTrue() {
+ Long networkId = 1L;
+ NetworkVO networkVO = Mockito.mock(NetworkVO.class);
+ Mockito.when(networkDao.findById(networkId)).thenReturn(networkVO);
+
Mockito.when(vlanDao.listVlansByNetworkId(networkId)).thenReturn(Collections.emptyList());
+
Mockito.when(routedIpv4Manager.isRoutedNetwork(networkVO)).thenReturn(true);
+
+
Assert.assertTrue(statsCollector.isNetworkEligibleForNetworkStats(networkId));
+ }
+
+ @Test
+ public void
isNetworkEligibleForNetworkStats_DirectAttachedNetwork_ReturnsTrue() {
+ Long networkId = 1L;
+ NetworkVO networkVO = Mockito.mock(NetworkVO.class);
+ Mockito.when(networkDao.findById(networkId)).thenReturn(networkVO);
+
Mockito.when(routedIpv4Manager.isRoutedNetwork(networkVO)).thenReturn(false);
+ List<VlanVO> vlans =
Collections.singletonList(buildVlan(VlanType.DirectAttached));
+
Mockito.when(vlanDao.listVlansByNetworkId(networkId)).thenReturn(vlans);
+
+
Assert.assertTrue(statsCollector.isNetworkEligibleForNetworkStats(networkId));
+ }
+
+ @Test
+ public void
isNetworkEligibleForNetworkStats_NeitherRoutedNorDirectAttached_ReturnsFalse() {
+ Long networkId = 1L;
+ NetworkVO networkVO = Mockito.mock(NetworkVO.class);
+ Mockito.when(networkDao.findById(networkId)).thenReturn(networkVO);
+
Mockito.when(routedIpv4Manager.isRoutedNetwork(networkVO)).thenReturn(false);
+ List<VlanVO> vlans =
Collections.singletonList(buildVlan(VlanType.VirtualNetwork));
+
Mockito.when(vlanDao.listVlansByNetworkId(networkId)).thenReturn(vlans);
+
+
Assert.assertFalse(statsCollector.isNetworkEligibleForNetworkStats(networkId));
+ }
+
+ @Test
+ public void
isNetworkEligibleForNetworkStats_NullNetworkAndEmptyVlans_ReturnsFalse() {
+ Long networkId = 1L;
+ Mockito.when(networkDao.findById(networkId)).thenReturn(null);
+
Mockito.when(vlanDao.listVlansByNetworkId(networkId)).thenReturn(Collections.emptyList());
+
+
Assert.assertFalse(statsCollector.isNetworkEligibleForNetworkStats(networkId));
+ }
+
+ @Test
+ public void
isNetworkEligibleForNetworkStats_NullNetworkButDirectAttached_ReturnsTrue() {
+ Long networkId = 1L;
+ Mockito.when(networkDao.findById(networkId)).thenReturn(null);
+ List<VlanVO> vlans =
Collections.singletonList(buildVlan(VlanType.DirectAttached));
+
Mockito.when(vlanDao.listVlansByNetworkId(networkId)).thenReturn(vlans);
+
+
Assert.assertTrue(statsCollector.isNetworkEligibleForNetworkStats(networkId));
+ Mockito.verify(routedIpv4Manager,
Mockito.never()).isRoutedNetwork(Mockito.any());
+ }
+
+ @Test
+ public void isNetworkEligibleForNetworkStats_NullNetworkId_ReturnsFalse() {
+
Assert.assertFalse(statsCollector.isNetworkEligibleForNetworkStats(null));
+ Mockito.verify(networkDao,
Mockito.never()).findById(Mockito.anyLong());
+ Mockito.verify(vlanDao,
Mockito.never()).listVlansByNetworkId(Mockito.anyLong());
+ }
+
private static class TestClass {
private String str;
private int num;