From 960cb84083d5a0293c3c595f06cf312560ba4bc9 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Mon, 12 Oct 2015 14:22:29 +0200 Subject: [PATCH] CLOUDSTACK-7984: Collect network statistics for VMs on shared network (KVM implementation) --- api/src/com/cloud/vm/UserVmService.java | 4 + api/src/com/cloud/vm/VmNetworkStats.java | 26 +++ .../cloud/agent/api/GetVmDiskStatsAnswer.java | 2 +- .../agent/api/GetVmNetworkStatsAnswer.java | 50 ++++++ .../agent/api/GetVmNetworkStatsCommand.java | 57 +++++++ .../cloud/agent/api/VmNetworkStatsEntry.java | 74 ++++++++ .../cloud/vm/VirtualMachineManagerImpl.java | 7 + .../resource/LibvirtComputingResource.java | 25 +++ ...ibvirtGetVmNetworkStatsCommandWrapper.java | 64 +++++++ .../vmware/resource/VmwareResource.java | 8 + ...CitrixGetVmNetworkStatsCommandWrapper.java | 36 ++++ .../src/com/cloud/api/ApiResponseHelper.java | 2 +- .../src/com/cloud/configuration/Config.java | 1 + .../src/com/cloud/server/StatsCollector.java | 149 +++++++++++++++++ .../cloud/storage/VolumeApiServiceImpl.java | 9 + server/src/com/cloud/vm/UserVmManager.java | 5 +- .../src/com/cloud/vm/UserVmManagerImpl.java | 158 +++++++++++++++++- 17 files changed, 671 insertions(+), 6 deletions(-) create mode 100644 api/src/com/cloud/vm/VmNetworkStats.java create mode 100644 core/src/com/cloud/agent/api/GetVmNetworkStatsAnswer.java create mode 100644 core/src/com/cloud/agent/api/GetVmNetworkStatsCommand.java create mode 100644 core/src/com/cloud/agent/api/VmNetworkStatsEntry.java create mode 100644 plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVmNetworkStatsCommandWrapper.java create mode 100644 plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixGetVmNetworkStatsCommandWrapper.java diff --git a/api/src/com/cloud/vm/UserVmService.java b/api/src/com/cloud/vm/UserVmService.java index db9ded89a98..68425c3b675 100644 --- a/api/src/com/cloud/vm/UserVmService.java +++ b/api/src/com/cloud/vm/UserVmService.java @@ -482,4 +482,8 @@ public interface UserVmService { */ public boolean isDisplayResourceEnabled(Long vmId); + void collectVmDiskStatistics(UserVm userVm); + + void collectVmNetworkStatistics (UserVm userVm); + } diff --git a/api/src/com/cloud/vm/VmNetworkStats.java b/api/src/com/cloud/vm/VmNetworkStats.java new file mode 100644 index 00000000000..7f96fdfdfac --- /dev/null +++ b/api/src/com/cloud/vm/VmNetworkStats.java @@ -0,0 +1,26 @@ +// 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.vm; + +public interface VmNetworkStats { + // vm related network stats + + public long getBytesSent(); + + public long getBytesReceived(); + +} diff --git a/core/src/com/cloud/agent/api/GetVmDiskStatsAnswer.java b/core/src/com/cloud/agent/api/GetVmDiskStatsAnswer.java index 77fe8ae62d2..cb52c462d65 100644 --- a/core/src/com/cloud/agent/api/GetVmDiskStatsAnswer.java +++ b/core/src/com/cloud/agent/api/GetVmDiskStatsAnswer.java @@ -24,7 +24,7 @@ import java.util.List; import com.cloud.agent.api.LogLevel.Log4jLevel; -@LogLevel(Log4jLevel.Trace) +@LogLevel(Log4jLevel.Debug) public class GetVmDiskStatsAnswer extends Answer { String hostName; diff --git a/core/src/com/cloud/agent/api/GetVmNetworkStatsAnswer.java b/core/src/com/cloud/agent/api/GetVmNetworkStatsAnswer.java new file mode 100644 index 00000000000..85a99cb1f41 --- /dev/null +++ b/core/src/com/cloud/agent/api/GetVmNetworkStatsAnswer.java @@ -0,0 +1,50 @@ +// +// 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.agent.api; + +import java.util.HashMap; +import java.util.List; + +import com.cloud.agent.api.LogLevel.Log4jLevel; + +@LogLevel(Log4jLevel.Debug) +public class GetVmNetworkStatsAnswer extends Answer { + + String hostName; + HashMap> vmNetworkStatsMap; + + public GetVmNetworkStatsAnswer(GetVmNetworkStatsCommand cmd, String details, String hostName, HashMap> vmNetworkStatsMap) { + super(cmd, true, details); + this.hostName = hostName; + this.vmNetworkStatsMap = vmNetworkStatsMap; + } + + public String getHostName() { + return hostName; + } + + public HashMap> getVmNetworkStatsMap() { + return vmNetworkStatsMap; + } + + protected GetVmNetworkStatsAnswer() { + //no-args constructor for json serialization-deserialization + } +} diff --git a/core/src/com/cloud/agent/api/GetVmNetworkStatsCommand.java b/core/src/com/cloud/agent/api/GetVmNetworkStatsCommand.java new file mode 100644 index 00000000000..266045e8115 --- /dev/null +++ b/core/src/com/cloud/agent/api/GetVmNetworkStatsCommand.java @@ -0,0 +1,57 @@ +// +// 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.agent.api; + +import java.util.List; + +import com.cloud.agent.api.LogLevel.Log4jLevel; + +@LogLevel(Log4jLevel.Trace) +public class GetVmNetworkStatsCommand extends Command { + List vmNames; + String hostGuid; + String hostName; + + protected GetVmNetworkStatsCommand() { + } + + public GetVmNetworkStatsCommand(List vmNames, String hostGuid, String hostName) { + this.vmNames = vmNames; + this.hostGuid = hostGuid; + this.hostName = hostName; + } + + public List getVmNames() { + return vmNames; + } + + public String getHostGuid() { + return this.hostGuid; + } + + public String getHostName() { + return this.hostName; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/com/cloud/agent/api/VmNetworkStatsEntry.java b/core/src/com/cloud/agent/api/VmNetworkStatsEntry.java new file mode 100644 index 00000000000..41db54a822e --- /dev/null +++ b/core/src/com/cloud/agent/api/VmNetworkStatsEntry.java @@ -0,0 +1,74 @@ +// +// 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.agent.api; + +import com.cloud.vm.VmNetworkStats; + +public class VmNetworkStatsEntry implements VmNetworkStats { + String vmName; + String macAddress; + long bytesSent; + long bytesReceived; + + public VmNetworkStatsEntry() { + } + + public VmNetworkStatsEntry(String vmName, String macAddress, long bytesSent, long bytesReceived) { + this.bytesSent = bytesSent; + this.bytesReceived = bytesReceived; + this.vmName = vmName; + this.macAddress = macAddress; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + public String getVmName() { + return vmName; + } + + public void setMacAddress(String macAddress) { + this.macAddress = macAddress; + } + + public String getMacAddress() { + return macAddress; + } + + public void setBytesSent(long bytesSent) { + this.bytesSent = bytesSent; + } + + @Override + public long getBytesSent() { + return bytesSent; + } + + public void setBytesReceived(long bytesReceived) { + this.bytesReceived = bytesReceived; + } + + @Override + public long getBytesReceived() { + return bytesReceived; + } + +} diff --git a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java index f370111207f..5127cf3feef 100755 --- a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -236,6 +236,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Inject protected UserVmDao _userVmDao; @Inject + protected UserVmService _userVmService; + @Inject protected CapacityManager _capacityMgr; @Inject protected NicDao _nicsDao; @@ -3637,6 +3639,11 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac final VMInstanceVO router = _vmDao.findById(vm.getId()); if (router.getState() == State.Running) { + // collect vm network statistics before unplug a nic + UserVmVO userVm = _userVmDao.findById(vm.getId()); + if (userVm != null && userVm.getType() == VirtualMachine.Type.User) { + _userVmService.collectVmNetworkStatistics(userVm); + } try { final Commands cmds = new Commands(Command.OnError.Stop); final UnPlugNicCommand unplugNicCmd = new UnPlugNicCommand(nic, vm.getName()); diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 58db46609cf..2440e624ffa 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -89,6 +89,7 @@ import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupRoutingCommand; import com.cloud.agent.api.StartupStorageCommand; import com.cloud.agent.api.VmDiskStatsEntry; +import com.cloud.agent.api.VmNetworkStatsEntry; import com.cloud.agent.api.VmStatsEntry; import com.cloud.agent.api.routing.IpAssocCommand; import com.cloud.agent.api.routing.IpAssocVpcCommand; @@ -3114,6 +3115,30 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv return command.execute(); } + public List getVmNetworkStat(Connect conn, String vmName) throws LibvirtException { + Domain dm = null; + try { + dm = getDomain(conn, vmName); + + List stats = new ArrayList(); + + List nics = getInterfaces(conn, vmName); + + for (InterfaceDef nic : nics) { + DomainInterfaceStats nicStats = dm.interfaceStats(nic.getDevName()); + String macAddress = nic.getMacAddress(); + VmNetworkStatsEntry stat = new VmNetworkStatsEntry(vmName, macAddress, nicStats.tx_bytes, nicStats.rx_bytes); + stats.add(stat); + } + + return stats; + } finally { + if (dm != null) { + dm.free(); + } + } + } + public List getVmDiskStat(final Connect conn, final String vmName) throws LibvirtException { Domain dm = null; try { diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVmNetworkStatsCommandWrapper.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVmNetworkStatsCommandWrapper.java new file mode 100644 index 00000000000..19a0a46bc7d --- /dev/null +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVmNetworkStatsCommandWrapper.java @@ -0,0 +1,64 @@ +// +// 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.hypervisor.kvm.resource.wrapper; + +import java.util.HashMap; +import java.util.List; + +import org.apache.log4j.Logger; +import org.libvirt.Connect; +import org.libvirt.LibvirtException; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.GetVmNetworkStatsAnswer; +import com.cloud.agent.api.GetVmNetworkStatsCommand; +import com.cloud.agent.api.VmNetworkStatsEntry; +import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; +import com.cloud.resource.CommandWrapper; +import com.cloud.resource.ResourceWrapper; + +@ResourceWrapper(handles = GetVmNetworkStatsCommand.class) +public final class LibvirtGetVmNetworkStatsCommandWrapper extends CommandWrapper { + + private static final Logger s_logger = Logger.getLogger(LibvirtGetVmNetworkStatsCommandWrapper.class); + + @Override + public Answer execute(final GetVmNetworkStatsCommand command, final LibvirtComputingResource libvirtComputingResource) { + final List vmNames = command.getVmNames(); + final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper(); + + try { + final HashMap> vmNetworkStatsNameMap = new HashMap>(); + final Connect conn = libvirtUtilitiesHelper.getConnection(); + for (final String vmName : vmNames) { + final List statEntry = libvirtComputingResource.getVmNetworkStat(conn, vmName); + if (statEntry == null) { + continue; + } + + vmNetworkStatsNameMap.put(vmName, statEntry); + } + return new GetVmNetworkStatsAnswer(command, "", command.getHostName(), vmNetworkStatsNameMap); + } catch (final LibvirtException e) { + s_logger.debug("Can't get vm network stats: " + e.toString()); + return new GetVmNetworkStatsAnswer(command, null, null, null); + } + } +} \ No newline at end of file diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 8b78534f218..bddd61ace01 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -139,6 +139,8 @@ import com.cloud.agent.api.GetStorageStatsAnswer; import com.cloud.agent.api.GetStorageStatsCommand; import com.cloud.agent.api.GetVmDiskStatsAnswer; import com.cloud.agent.api.GetVmDiskStatsCommand; +import com.cloud.agent.api.GetVmNetworkStatsAnswer; +import com.cloud.agent.api.GetVmNetworkStatsCommand; import com.cloud.agent.api.GetVmStatsAnswer; import com.cloud.agent.api.GetVmStatsCommand; import com.cloud.agent.api.GetVncPortAnswer; @@ -407,6 +409,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa answer = execute((GetHostStatsCommand)cmd); } else if (clz == GetVmStatsCommand.class) { answer = execute((GetVmStatsCommand)cmd); + } else if (clz == GetVmNetworkStatsCommand.class) { + answer = execute((GetVmNetworkStatsCommand) cmd); } else if (clz == GetVmDiskStatsCommand.class) { answer = execute((GetVmDiskStatsCommand)cmd); } else if (clz == CheckHealthCommand.class) { @@ -3206,6 +3210,10 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa return new GetVmDiskStatsAnswer(cmd, null, null, null); } + protected Answer execute(GetVmNetworkStatsCommand cmd) { + return new GetVmNetworkStatsAnswer(cmd, null, null, null); + } + protected Answer execute(CheckHealthCommand cmd) { if (s_logger.isInfoEnabled()) { s_logger.info("Executing resource CheckHealthCommand: " + _gson.toJson(cmd)); diff --git a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixGetVmNetworkStatsCommandWrapper.java b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixGetVmNetworkStatsCommandWrapper.java new file mode 100644 index 00000000000..45cacf1500f --- /dev/null +++ b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixGetVmNetworkStatsCommandWrapper.java @@ -0,0 +1,36 @@ +// +// 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.hypervisor.xenserver.resource.wrapper.xenbase; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.GetVmNetworkStatsAnswer; +import com.cloud.agent.api.GetVmNetworkStatsCommand; +import com.cloud.hypervisor.xenserver.resource.CitrixResourceBase; +import com.cloud.resource.CommandWrapper; +import com.cloud.resource.ResourceWrapper; + +@ResourceWrapper(handles = GetVmNetworkStatsCommand.class) +public final class CitrixGetVmNetworkStatsCommandWrapper extends CommandWrapper { + + @Override + public Answer execute(final GetVmNetworkStatsCommand command, final CitrixResourceBase citrixResourceBase) { + return new GetVmNetworkStatsAnswer(command, null, null, null); + } +} \ No newline at end of file diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 9a0991707f0..fd7c67db29d 100644 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -3266,7 +3266,7 @@ public class ApiResponseHelper implements ResponseGenerator { } else if (usageRecord.getUsageType() == UsageTypes.NETWORK_BYTES_SENT || usageRecord.getUsageType() == UsageTypes.NETWORK_BYTES_RECEIVED) { //Device Type usageRecResponse.setType(usageRecord.getType()); - if (usageRecord.getType().equals("DomainRouter")) { + if (usageRecord.getType().equals("DomainRouter") || usageRecord.getType().equals("UserVm")) { //Domain Router Id VMInstanceVO vm = _entityMgr.findByIdIncludingRemoved(VMInstanceVO.class, usageRecord.getUsageId().toString()); if (vm != null) { diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 50e3e7a0c80..b607502c317 100644 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -869,6 +869,7 @@ public enum Config { "The interval (in milliseconds) when vm stats are retrieved from agents.", null), VmDiskStatsInterval("Advanced", ManagementServer.class, Integer.class, "vm.disk.stats.interval", "0", "Interval (in seconds) to report vm disk statistics.", null), + VmNetworkStatsInterval("Advanced", ManagementServer.class, Integer.class, "vm.network.stats.interval", "0", "Interval (in seconds) to report vm network statistics (for Shared networks).", null), VmTransitionWaitInterval( "Advanced", ManagementServer.class, diff --git a/server/src/com/cloud/server/StatsCollector.java b/server/src/com/cloud/server/StatsCollector.java index f47123de1ae..04313013c79 100644 --- a/server/src/com/cloud/server/StatsCollector.java +++ b/server/src/com/cloud/server/StatsCollector.java @@ -62,9 +62,13 @@ import com.cloud.agent.api.HostStatsEntry; import com.cloud.agent.api.PerformanceMonitorCommand; import com.cloud.agent.api.VgpuTypesInfo; import com.cloud.agent.api.VmDiskStatsEntry; +import com.cloud.agent.api.VmNetworkStatsEntry; import com.cloud.agent.api.VmStatsEntry; import com.cloud.cluster.ManagementServerHostVO; import com.cloud.cluster.dao.ManagementServerHostDao; +import com.cloud.dc.Vlan.VlanType; +import com.cloud.dc.VlanVO; +import com.cloud.dc.dao.VlanDao; import com.cloud.exception.StorageUnavailableException; import com.cloud.gpu.dao.HostGpuGroupsDao; import com.cloud.host.Host; @@ -103,7 +107,9 @@ import com.cloud.storage.VolumeStats; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.dao.VolumeDao; +import com.cloud.user.UserStatisticsVO; import com.cloud.user.VmDiskStatisticsVO; +import com.cloud.user.dao.UserStatisticsDao; import com.cloud.user.dao.VmDiskStatisticsDao; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; @@ -117,11 +123,13 @@ import com.cloud.utils.db.Transaction; import com.cloud.utils.db.TransactionCallbackNoReturn; import com.cloud.utils.db.TransactionStatus; import com.cloud.utils.net.MacAddress; +import com.cloud.vm.NicVO; import com.cloud.vm.UserVmManager; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VmStats; +import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; @@ -186,6 +194,12 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc @Inject private ManagementServerHostDao _msHostDao; @Inject + private UserStatisticsDao _userStatsDao; + @Inject + private NicDao _nicDao; + @Inject + private VlanDao _vlanDao; + @Inject private AutoScaleVmGroupDao _asGroupDao; @Inject private AutoScaleVmGroupVmMapDao _asGroupVmDao; @@ -225,6 +239,7 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc long volumeStatsInterval = -1L; long autoScaleStatsInterval = -1L; int vmDiskStatsInterval = 0; + int vmNetworkStatsInterval = 0; List hostIds = null; private double _imageStoreCapacityThreshold = 0.90; @@ -272,6 +287,7 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc volumeStatsInterval = NumbersUtil.parseLong(configs.get("volume.stats.interval"), -1L); autoScaleStatsInterval = NumbersUtil.parseLong(configs.get("autoscale.stats.interval"), 60000L); vmDiskStatsInterval = NumbersUtil.parseInt(configs.get("vm.disk.stats.interval"), 0); + vmNetworkStatsInterval = NumbersUtil.parseInt(configs.get("vm.network.stats.interval"), 0); /* URI to send statistics to. Currently only Graphite is supported */ String externalStatsUri = configs.get("stats.output.uri"); @@ -335,8 +351,17 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc _executor.scheduleAtFixedRate(new VmDiskStatsTask(), vmDiskStatsInterval, vmDiskStatsInterval, TimeUnit.SECONDS); } + if (vmNetworkStatsInterval > 0) { + if (vmNetworkStatsInterval < 300) + vmNetworkStatsInterval = 300; + _executor.scheduleAtFixedRate(new VmNetworkStatsTask(), vmNetworkStatsInterval, vmNetworkStatsInterval, TimeUnit.SECONDS); + } else { + s_logger.debug("vm.network.stats.interval - " + vmNetworkStatsInterval + " so not scheduling the vm network stats thread"); + } + //Schedule disk stats update task _diskStatsUpdateExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DiskStatsUpdater")); + String aggregationRange = configs.get("usage.stats.job.aggregation.range"); _usageAggregationRange = NumbersUtil.parseInt(aggregationRange, 1440); _usageTimeZone = configs.get("usage.aggregation.timezone"); @@ -641,11 +666,20 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc class VmDiskStatsTask extends ManagedContextRunnable { @Override protected void runInContext() { + //Check for ownership + //msHost in UP state with min id should run the job + ManagementServerHostVO msHost = _msHostDao.findOneInUpState(new Filter(ManagementServerHostVO.class, "id", true, 0L, 1L)); + if(msHost == null || (msHost.getMsid() != mgmtSrvrId)){ + s_logger.debug("Skipping collect vm disk stats from hosts"); + return; + } // collect the vm disk statistics(total) from hypervisor. added by weizhou, 2013.03. try { Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { + s_logger.debug("VmDiskStatsTask is running..."); + SearchCriteria sc = _hostDao.createSearchCriteria(); sc.addAnd("status", SearchCriteria.Op.EQ, Status.Up.toString()); sc.addAnd("resourceState", SearchCriteria.Op.NIN, ResourceState.Maintenance, ResourceState.PrepareForMaintenance, @@ -763,6 +797,121 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc } } + class VmNetworkStatsTask extends ManagedContextRunnable { + @Override + protected void runInContext() { + //Check for ownership + //msHost in UP state with min id should run the job + ManagementServerHostVO msHost = _msHostDao.findOneInUpState(new Filter(ManagementServerHostVO.class, "id", true, 0L, 1L)); + if(msHost == null || (msHost.getMsid() != mgmtSrvrId)){ + s_logger.debug("Skipping collect vm network stats from hosts"); + return; + } + // collect the vm network statistics(total) from hypervisor + try { + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + s_logger.debug("VmNetworkStatsTask is running..."); + + SearchCriteria sc = _hostDao.createSearchCriteria(); + sc.addAnd("status", SearchCriteria.Op.EQ, Status.Up.toString()); + sc.addAnd("resourceState", SearchCriteria.Op.NIN, ResourceState.Maintenance, ResourceState.PrepareForMaintenance, ResourceState.ErrorInMaintenance); + sc.addAnd("type", SearchCriteria.Op.EQ, Host.Type.Routing.toString()); + List hosts = _hostDao.search(sc, null); + + for (HostVO host : hosts) + { + List vms = _userVmDao.listRunningByHostId(host.getId()); + List vmIds = new ArrayList(); + + for (UserVmVO vm : vms) { + if (vm.getType() == VirtualMachine.Type.User) // user vm + vmIds.add(vm.getId()); + } + + HashMap> vmNetworkStatsById = _userVmMgr.getVmNetworkStatistics(host.getId(), host.getName(), vmIds); + if (vmNetworkStatsById == null) + continue; + + Set vmIdSet = vmNetworkStatsById.keySet(); + for(Long vmId : vmIdSet) + { + List vmNetworkStats = vmNetworkStatsById.get(vmId); + if (vmNetworkStats == null) + continue; + UserVmVO userVm = _userVmDao.findById(vmId); + for (VmNetworkStatsEntry vmNetworkStat:vmNetworkStats) { + SearchCriteria sc_nic = _nicDao.createSearchCriteria(); + sc_nic.addAnd("macAddress", SearchCriteria.Op.EQ, vmNetworkStat.getMacAddress()); + NicVO nic = _nicDao.search(sc_nic, null).get(0); + List 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) + UserStatisticsVO previousvmNetworkStats = _userStatsDao.findBy(userVm.getAccountId(), userVm.getDataCenterId(), nic.getNetworkId(), nic.getIPv4Address(), vmId, "UserVm"); + if (previousvmNetworkStats == null) { + previousvmNetworkStats = new UserStatisticsVO(userVm.getAccountId(), userVm.getDataCenterId(),nic.getIPv4Address(), vmId, "UserVm", nic.getNetworkId()); + _userStatsDao.persist(previousvmNetworkStats); + } + UserStatisticsVO vmNetworkStat_lock = _userStatsDao.lock(userVm.getAccountId(), userVm.getDataCenterId(), nic.getNetworkId(), nic.getIPv4Address(), vmId, "UserVm"); + + if ((vmNetworkStat.getBytesSent() == 0) && (vmNetworkStat.getBytesReceived() == 0)) { + s_logger.debug("bytes sent and received are all 0. Not updating user_statistics"); + continue; + } + + if (vmNetworkStat_lock == null) { + s_logger.warn("unable to find vm network stats from host for account: " + userVm.getAccountId() + " with vmId: " + userVm.getId()+ " and nicId:" + nic.getId()); + continue; + } + + if (previousvmNetworkStats != null + && ((previousvmNetworkStats.getCurrentBytesSent() != vmNetworkStat_lock.getCurrentBytesSent()) + || (previousvmNetworkStats.getCurrentBytesReceived() != vmNetworkStat_lock.getCurrentBytesReceived()))) { + s_logger.debug("vm network stats changed from the time GetNmNetworkStatsCommand was sent. " + + "Ignoring current answer. Host: " + host.getName() + " . VM: " + vmNetworkStat.getVmName() + + " Sent(Bytes): " + vmNetworkStat.getBytesSent() + " Received(Bytes): " + vmNetworkStat.getBytesReceived()); + continue; + } + + if (vmNetworkStat_lock.getCurrentBytesSent() > vmNetworkStat.getBytesSent()) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Sent # of bytes that's less than the last one. " + + "Assuming something went wrong and persisting it. Host: " + host.getName() + " . VM: " + vmNetworkStat.getVmName() + + " Reported: " + vmNetworkStat.getBytesSent() + " Stored: " + vmNetworkStat_lock.getCurrentBytesSent()); + } + vmNetworkStat_lock.setNetBytesSent(vmNetworkStat_lock.getNetBytesSent() + vmNetworkStat_lock.getCurrentBytesSent()); + } + vmNetworkStat_lock.setCurrentBytesSent(vmNetworkStat.getBytesSent()); + + if (vmNetworkStat_lock.getCurrentBytesReceived() > vmNetworkStat.getBytesReceived()) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Received # of bytes that's less than the last one. " + + "Assuming something went wrong and persisting it. Host: " + host.getName() + " . VM: " + vmNetworkStat.getVmName() + + " Reported: " + vmNetworkStat.getBytesReceived() + " Stored: " + vmNetworkStat_lock.getCurrentBytesReceived()); + } + vmNetworkStat_lock.setNetBytesReceived(vmNetworkStat_lock.getNetBytesReceived() + vmNetworkStat_lock.getCurrentBytesReceived()); + } + vmNetworkStat_lock.setCurrentBytesReceived(vmNetworkStat.getBytesReceived()); + + if (! _dailyOrHourly) { + //update agg bytes + vmNetworkStat_lock.setAggBytesReceived(vmNetworkStat_lock.getNetBytesReceived() + vmNetworkStat_lock.getCurrentBytesReceived()); + vmNetworkStat_lock.setAggBytesSent(vmNetworkStat_lock.getNetBytesSent() + vmNetworkStat_lock.getCurrentBytesSent()); + } + + _userStatsDao.update(vmNetworkStat_lock.getId(), vmNetworkStat_lock); + } + } + } + } + }); + } catch (Exception e) { + s_logger.warn("Error while collecting vm network stats from hosts", e); + } + } + } + class StorageCollector extends ManagedContextRunnable { @Override protected void runInContext() { diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java index 98fd1a327ca..3330cc74a26 100644 --- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java @@ -84,6 +84,7 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.NoTransitionException; import com.cloud.utils.fsm.StateMachine2; import com.cloud.vm.UserVmManager; +import com.cloud.vm.UserVmService; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; @@ -205,6 +206,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic @Inject UserVmDao _userVmDao; @Inject + UserVmService _userVmService; + @Inject VolumeDataStoreDao _volumeStoreDao; @Inject VMInstanceDao _vmInstanceDao; @@ -1799,6 +1802,12 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic Answer answer = null; if (sendCommand) { + // collect vm disk statistics before detach a volume + UserVmVO userVm = _userVmDao.findById(vmId); + if (userVm != null && userVm.getType() == VirtualMachine.Type.User) { + _userVmService.collectVmDiskStatistics(userVm); + } + DataTO volTO = volFactory.getVolume(volume.getId()).getTO(); DiskTO disk = new DiskTO(volTO, volume.getDeviceId(), volume.getPath(), volume.getVolumeType()); diff --git a/server/src/com/cloud/vm/UserVmManager.java b/server/src/com/cloud/vm/UserVmManager.java index 411fd9b669f..51cce9d73a8 100644 --- a/server/src/com/cloud/vm/UserVmManager.java +++ b/server/src/com/cloud/vm/UserVmManager.java @@ -24,6 +24,7 @@ import org.apache.cloudstack.api.BaseCmd.HTTPMethod; import org.apache.cloudstack.framework.config.ConfigKey; import com.cloud.agent.api.VmDiskStatsEntry; +import com.cloud.agent.api.VmNetworkStatsEntry; import com.cloud.agent.api.VmStatsEntry; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; @@ -100,8 +101,6 @@ public interface UserVmManager extends UserVmService { boolean setupVmForPvlan(boolean add, Long hostId, NicProfile nic); - void collectVmDiskStatistics(UserVmVO userVm); - UserVm updateVirtualMachine(long id, String displayName, String group, Boolean ha, Boolean isDisplayVmEnabled, Long osTypeId, String userData, Boolean isDynamicallyScalable, HTTPMethod httpMethod, String customId, String hostName, String instanceName, List securityGroupIdList) throws ResourceUnavailableException, InsufficientCapacityException; @@ -116,4 +115,6 @@ public interface UserVmManager extends UserVmService { void generateUsageEvent(VirtualMachine vm, boolean isDisplay, String eventType); void persistDeviceBusInfo(UserVmVO paramUserVmVO, String paramString); + + HashMap> getVmNetworkStatistics(long hostId, String hostName, List vmIds); } diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 3a47f381078..5b3c5ba5a8b 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -99,6 +99,8 @@ import com.cloud.agent.api.Command; import com.cloud.agent.api.GetVmDiskStatsAnswer; import com.cloud.agent.api.GetVmDiskStatsCommand; import com.cloud.agent.api.GetVmIpAddressCommand; +import com.cloud.agent.api.GetVmNetworkStatsAnswer; +import com.cloud.agent.api.GetVmNetworkStatsCommand; import com.cloud.agent.api.GetVmStatsAnswer; import com.cloud.agent.api.GetVmStatsCommand; import com.cloud.agent.api.PvlanSetupCommand; @@ -106,6 +108,7 @@ import com.cloud.agent.api.RestoreVMSnapshotAnswer; import com.cloud.agent.api.RestoreVMSnapshotCommand; import com.cloud.agent.api.StartAnswer; import com.cloud.agent.api.VmDiskStatsEntry; +import com.cloud.agent.api.VmNetworkStatsEntry; import com.cloud.agent.api.VmStatsEntry; import com.cloud.agent.api.to.DiskTO; import com.cloud.agent.api.to.NicTO; @@ -128,6 +131,9 @@ import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.DedicatedResourceDao; import com.cloud.dc.dao.HostPodDao; +import com.cloud.dc.dao.VlanDao; +import com.cloud.dc.Vlan.VlanType; +import com.cloud.dc.VlanVO; import com.cloud.deploy.DataCenterDeployment; import com.cloud.deploy.DeployDestination; import com.cloud.deploy.DeploymentPlanner; @@ -248,11 +254,13 @@ import com.cloud.user.ResourceLimitService; import com.cloud.user.SSHKeyPair; import com.cloud.user.SSHKeyPairVO; import com.cloud.user.User; +import com.cloud.user.UserStatisticsVO; import com.cloud.user.UserVO; import com.cloud.user.VmDiskStatisticsVO; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.SSHKeyPairDao; import com.cloud.user.dao.UserDao; +import com.cloud.user.dao.UserStatisticsDao; import com.cloud.user.dao.VmDiskStatisticsDao; import com.cloud.uservm.UserVm; import com.cloud.utils.DateUtil; @@ -454,6 +462,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Inject private ServiceOfferingDetailsDao serviceOfferingDetailsDao; @Inject + private UserStatisticsDao _userStatsDao; + @Inject + private VlanDao _vlanDao; + @Inject VolumeService _volService; @Inject VolumeDataFactory volFactory; @@ -902,6 +914,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (vm.getState() == State.Running && vm.getHostId() != null) { collectVmDiskStatistics(vm); + collectVmNetworkStatistics(vm); DataCenterVO dc = _dcDao.findById(vm.getDataCenterId()); try { if (dc.getNetworkType() == DataCenter.NetworkType.Advanced) { @@ -3689,6 +3702,144 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } } + @Override + public HashMap> getVmNetworkStatistics(long hostId, String hostName, List vmIds) { + HashMap> vmNetworkStatsById = new HashMap>(); + + if (vmIds.isEmpty()) { + return vmNetworkStatsById; + } + + List vmNames = new ArrayList(); + + for (Long vmId : vmIds) { + UserVmVO vm = _vmDao.findById(vmId); + vmNames.add(vm.getInstanceName()); + } + + Answer answer = _agentMgr.easySend(hostId, new GetVmNetworkStatsCommand(vmNames, _hostDao.findById(hostId).getGuid(), hostName)); + if (answer == null || !answer.getResult()) { + s_logger.warn("Unable to obtain VM network statistics."); + return null; + } else { + HashMap> vmNetworkStatsByName = ((GetVmNetworkStatsAnswer)answer).getVmNetworkStatsMap(); + + if (vmNetworkStatsByName == null) { + s_logger.warn("Unable to obtain VM network statistics."); + return null; + } + + for (String vmName : vmNetworkStatsByName.keySet()) { + vmNetworkStatsById.put(vmIds.get(vmNames.indexOf(vmName)), vmNetworkStatsByName.get(vmName)); + } + } + + return vmNetworkStatsById; + } + + @Override + public void collectVmNetworkStatistics (final UserVm userVm) { + if (!userVm.getHypervisorType().equals(HypervisorType.KVM)) + return; + s_logger.debug("Collect vm network statistics from host before stopping Vm"); + long hostId = userVm.getHostId(); + List vmNames = new ArrayList(); + vmNames.add(userVm.getInstanceName()); + final HostVO host = _hostDao.findById(hostId); + + GetVmNetworkStatsAnswer networkStatsAnswer = null; + try { + networkStatsAnswer = (GetVmNetworkStatsAnswer) _agentMgr.easySend(hostId, new GetVmNetworkStatsCommand(vmNames, host.getGuid(), host.getName())); + } catch (Exception e) { + s_logger.warn("Error while collecting network stats for vm: " + userVm.getHostName() + " from host: " + host.getName(), e); + return; + } + if (networkStatsAnswer != null) { + if (!networkStatsAnswer.getResult()) { + s_logger.warn("Error while collecting network stats vm: " + userVm.getHostName() + " from host: " + host.getName() + "; details: " + networkStatsAnswer.getDetails()); + return; + } + try { + final GetVmNetworkStatsAnswer networkStatsAnswerFinal = networkStatsAnswer; + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + HashMap> vmNetworkStatsByName = networkStatsAnswerFinal.getVmNetworkStatsMap(); + if (vmNetworkStatsByName == null) + return; + List vmNetworkStats = vmNetworkStatsByName.get(userVm.getInstanceName()); + if (vmNetworkStats == null) + return; + + for (VmNetworkStatsEntry vmNetworkStat:vmNetworkStats) { + SearchCriteria sc_nic = _nicDao.createSearchCriteria(); + sc_nic.addAnd("macAddress", SearchCriteria.Op.EQ, vmNetworkStat.getMacAddress()); + NicVO nic = _nicDao.search(sc_nic, null).get(0); + List vlan = _vlanDao.listVlansByNetworkId(nic.getNetworkId()); + if (vlan == null || vlan.size() == 0 || vlan.get(0).getVlanType() != VlanType.DirectAttached) + break; // only get network statistics for DirectAttached network (shared networks in Basic zone and Advanced zone with/without SG) + UserStatisticsVO previousvmNetworkStats = _userStatsDao.findBy(userVm.getAccountId(), userVm.getDataCenterId(), nic.getNetworkId(), nic.getIPv4Address(), userVm.getId(), "UserVm"); + if (previousvmNetworkStats == null) { + previousvmNetworkStats = new UserStatisticsVO(userVm.getAccountId(), userVm.getDataCenterId(),nic.getIPv4Address(), userVm.getId(), "UserVm", nic.getNetworkId()); + _userStatsDao.persist(previousvmNetworkStats); + } + UserStatisticsVO vmNetworkStat_lock = _userStatsDao.lock(userVm.getAccountId(), userVm.getDataCenterId(), nic.getNetworkId(), nic.getIPv4Address(), userVm.getId(), "UserVm"); + + if ((vmNetworkStat.getBytesSent() == 0) && (vmNetworkStat.getBytesReceived() == 0)) { + s_logger.debug("bytes sent and received are all 0. Not updating user_statistics"); + continue; + } + + if (vmNetworkStat_lock == null) { + s_logger.warn("unable to find vm network stats from host for account: " + userVm.getAccountId() + " with vmId: " + userVm.getId()+ " and nicId:" + nic.getId()); + continue; + } + + if (previousvmNetworkStats != null + && ((previousvmNetworkStats.getCurrentBytesSent() != vmNetworkStat_lock.getCurrentBytesSent()) + || (previousvmNetworkStats.getCurrentBytesReceived() != vmNetworkStat_lock.getCurrentBytesReceived()))) { + s_logger.debug("vm network stats changed from the time GetNmNetworkStatsCommand was sent. " + + "Ignoring current answer. Host: " + host.getName() + " . VM: " + vmNetworkStat.getVmName() + + " Sent(Bytes): " + vmNetworkStat.getBytesSent() + " Received(Bytes): " + vmNetworkStat.getBytesReceived()); + continue; + } + + if (vmNetworkStat_lock.getCurrentBytesSent() > vmNetworkStat.getBytesSent()) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Sent # of bytes that's less than the last one. " + + "Assuming something went wrong and persisting it. Host: " + host.getName() + " . VM: " + vmNetworkStat.getVmName() + + " Reported: " + vmNetworkStat.getBytesSent() + " Stored: " + vmNetworkStat_lock.getCurrentBytesSent()); + } + vmNetworkStat_lock.setNetBytesSent(vmNetworkStat_lock.getNetBytesSent() + vmNetworkStat_lock.getCurrentBytesSent()); + } + vmNetworkStat_lock.setCurrentBytesSent(vmNetworkStat.getBytesSent()); + + if (vmNetworkStat_lock.getCurrentBytesReceived() > vmNetworkStat.getBytesReceived()) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Received # of bytes that's less than the last one. " + + "Assuming something went wrong and persisting it. Host: " + host.getName() + " . VM: " + vmNetworkStat.getVmName() + + " Reported: " + vmNetworkStat.getBytesReceived() + " Stored: " + vmNetworkStat_lock.getCurrentBytesReceived()); + } + vmNetworkStat_lock.setNetBytesReceived(vmNetworkStat_lock.getNetBytesReceived() + vmNetworkStat_lock.getCurrentBytesReceived()); + } + vmNetworkStat_lock.setCurrentBytesReceived(vmNetworkStat.getBytesReceived()); + + if (! _dailyOrHourly) { + //update agg bytes + vmNetworkStat_lock.setAggBytesReceived(vmNetworkStat_lock.getNetBytesReceived() + vmNetworkStat_lock.getCurrentBytesReceived()); + vmNetworkStat_lock.setAggBytesSent(vmNetworkStat_lock.getNetBytesSent() + vmNetworkStat_lock.getCurrentBytesSent()); + } + + _userStatsDao.update(vmNetworkStat_lock.getId(), vmNetworkStat_lock); + } + } + }); + } catch (Exception e) { + s_logger.warn("Unable to update vm network statistics for vm: " + userVm.getId() + " from host: " + hostId, e); + } + } + } + private void validateUserData(String userData, HTTPMethod httpmethod) { byte[] decodedUserData = null; if (userData != null) { @@ -4232,7 +4383,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } @Override - public void collectVmDiskStatistics(final UserVmVO userVm) { + public void collectVmDiskStatistics(final UserVm userVm) { // support KVM only util 2013.06.25 if (!userVm.getHypervisorType().equals(HypervisorType.KVM)) return; @@ -4748,6 +4899,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir UserVmVO uservm = _vmDao.findById(vmId); if (uservm != null) { collectVmDiskStatistics(uservm); + collectVmNetworkStatistics(uservm); } _itMgr.migrate(vm.getUuid(), srcHostId, dest); VMInstanceVO vmInstance = _vmInstanceDao.findById(vmId); @@ -5871,8 +6023,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Override public void prepareStop(VirtualMachineProfile profile) { UserVmVO vm = _vmDao.findById(profile.getId()); - if (vm != null && vm.getState() == State.Stopping) + if (vm != null && vm.getState() == State.Stopping) { collectVmDiskStatistics(vm); + collectVmNetworkStatistics(vm); + } } private void encryptAndStorePassword(UserVmVO vm, String password) {