CLOUDSTACK-7984: Collect network statistics for VMs on shared network (KVM implementation)

This commit is contained in:
Wei Zhou 2015-10-12 14:22:29 +02:00 committed by Rohit Yadav
parent 61208b5db2
commit 960cb84083
17 changed files with 671 additions and 6 deletions

View File

@ -482,4 +482,8 @@ public interface UserVmService {
*/
public boolean isDisplayResourceEnabled(Long vmId);
void collectVmDiskStatistics(UserVm userVm);
void collectVmNetworkStatistics (UserVm userVm);
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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<String, List<VmNetworkStatsEntry>> vmNetworkStatsMap;
public GetVmNetworkStatsAnswer(GetVmNetworkStatsCommand cmd, String details, String hostName, HashMap<String, List<VmNetworkStatsEntry>> vmNetworkStatsMap) {
super(cmd, true, details);
this.hostName = hostName;
this.vmNetworkStatsMap = vmNetworkStatsMap;
}
public String getHostName() {
return hostName;
}
public HashMap<String, List<VmNetworkStatsEntry>> getVmNetworkStatsMap() {
return vmNetworkStatsMap;
}
protected GetVmNetworkStatsAnswer() {
//no-args constructor for json serialization-deserialization
}
}

View File

@ -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<String> vmNames;
String hostGuid;
String hostName;
protected GetVmNetworkStatsCommand() {
}
public GetVmNetworkStatsCommand(List<String> vmNames, String hostGuid, String hostName) {
this.vmNames = vmNames;
this.hostGuid = hostGuid;
this.hostName = hostName;
}
public List<String> getVmNames() {
return vmNames;
}
public String getHostGuid() {
return this.hostGuid;
}
public String getHostName() {
return this.hostName;
}
@Override
public boolean executeInSequence() {
return false;
}
}

View File

@ -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;
}
}

View File

@ -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());

View File

@ -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<VmNetworkStatsEntry> getVmNetworkStat(Connect conn, String vmName) throws LibvirtException {
Domain dm = null;
try {
dm = getDomain(conn, vmName);
List<VmNetworkStatsEntry> stats = new ArrayList<VmNetworkStatsEntry>();
List<InterfaceDef> 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<VmDiskStatsEntry> getVmDiskStat(final Connect conn, final String vmName) throws LibvirtException {
Domain dm = null;
try {

View File

@ -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<GetVmNetworkStatsCommand, Answer, LibvirtComputingResource> {
private static final Logger s_logger = Logger.getLogger(LibvirtGetVmNetworkStatsCommandWrapper.class);
@Override
public Answer execute(final GetVmNetworkStatsCommand command, final LibvirtComputingResource libvirtComputingResource) {
final List<String> vmNames = command.getVmNames();
final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper();
try {
final HashMap<String, List<VmNetworkStatsEntry>> vmNetworkStatsNameMap = new HashMap<String, List<VmNetworkStatsEntry>>();
final Connect conn = libvirtUtilitiesHelper.getConnection();
for (final String vmName : vmNames) {
final List<VmNetworkStatsEntry> 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);
}
}
}

View File

@ -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));

View File

@ -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<GetVmNetworkStatsCommand, Answer, CitrixResourceBase> {
@Override
public Answer execute(final GetVmNetworkStatsCommand command, final CitrixResourceBase citrixResourceBase) {
return new GetVmNetworkStatsAnswer(command, null, null, null);
}
}

View File

@ -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) {

View File

@ -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,

View File

@ -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<Long> 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<HostVO> 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<HostVO> 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<HostVO> hosts = _hostDao.search(sc, null);
for (HostVO host : hosts)
{
List<UserVmVO> vms = _userVmDao.listRunningByHostId(host.getId());
List<Long> vmIds = new ArrayList<Long>();
for (UserVmVO vm : vms) {
if (vm.getType() == VirtualMachine.Type.User) // user vm
vmIds.add(vm.getId());
}
HashMap<Long, List<VmNetworkStatsEntry>> vmNetworkStatsById = _userVmMgr.getVmNetworkStatistics(host.getId(), host.getName(), vmIds);
if (vmNetworkStatsById == null)
continue;
Set<Long> vmIdSet = vmNetworkStatsById.keySet();
for(Long vmId : vmIdSet)
{
List<VmNetworkStatsEntry> vmNetworkStats = vmNetworkStatsById.get(vmId);
if (vmNetworkStats == null)
continue;
UserVmVO userVm = _userVmDao.findById(vmId);
for (VmNetworkStatsEntry vmNetworkStat:vmNetworkStats) {
SearchCriteria<NicVO> sc_nic = _nicDao.createSearchCriteria();
sc_nic.addAnd("macAddress", SearchCriteria.Op.EQ, vmNetworkStat.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)
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() {

View File

@ -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());

View File

@ -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<Long> 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<Long, List<VmNetworkStatsEntry>> getVmNetworkStatistics(long hostId, String hostName, List<Long> vmIds);
}

View File

@ -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<Long, List<VmNetworkStatsEntry>> getVmNetworkStatistics(long hostId, String hostName, List<Long> vmIds) {
HashMap<Long, List<VmNetworkStatsEntry>> vmNetworkStatsById = new HashMap<Long, List<VmNetworkStatsEntry>>();
if (vmIds.isEmpty()) {
return vmNetworkStatsById;
}
List<String> vmNames = new ArrayList<String>();
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<String, List<VmNetworkStatsEntry>> 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<String> vmNames = new ArrayList<String>();
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<String, List<VmNetworkStatsEntry>> vmNetworkStatsByName = networkStatsAnswerFinal.getVmNetworkStatsMap();
if (vmNetworkStatsByName == null)
return;
List<VmNetworkStatsEntry> vmNetworkStats = vmNetworkStatsByName.get(userVm.getInstanceName());
if (vmNetworkStats == null)
return;
for (VmNetworkStatsEntry vmNetworkStat:vmNetworkStats) {
SearchCriteria<NicVO> sc_nic = _nicDao.createSearchCriteria();
sc_nic.addAnd("macAddress", SearchCriteria.Op.EQ, vmNetworkStat.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)
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) {