Bug 13224 - Network Usage - Netscaler - External device is being queried multiple times (up to 8 times) whenever the Network Usage task is run.

Reviewed-by: Kishan

Changes:
- Separated out the External Network Usage task from the ExternalLBDeviceMgr because ExternalLbDeviceMgrImpl :: start() was getting multiple times during management server satrtup. The reason for this is that this is the baseclass for F5 and NetScalarElement.
- This caused us to schedule the ExternalNetworkUsageTask multiple times
- Also we have LBRulesMgr calling this ExternalLbDeviceMgrImpl  by creating an instance of this class which is declared abstract
- Hence having a separate implementation to manage the network usage stats should solve this.
This commit is contained in:
prachi 2012-02-21 16:46:53 -08:00
parent 4d4af60690
commit 760c62eb15
6 changed files with 587 additions and 410 deletions

View File

@ -72,7 +72,7 @@ import com.cloud.keystore.KeystoreDaoImpl;
import com.cloud.keystore.KeystoreManagerImpl;
import com.cloud.maint.UpgradeManagerImpl;
import com.cloud.maint.dao.AgentUpgradeDaoImpl;
import com.cloud.network.ExternalLoadBalancerDeviceManagerImpl;
import com.cloud.network.ExternalLoadBalancerUsageManagerImpl;
import com.cloud.network.NetworkManagerImpl;
import com.cloud.network.StorageNetworkManagerImpl;
import com.cloud.network.dao.ExternalFirewallDeviceDaoImpl;
@ -388,7 +388,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com
addManager("ElasticLoadBalancerManager", ElasticLoadBalancerManagerImpl.class);
addManager("SwiftManager", SwiftManagerImpl.class);
addManager("StorageNetworkManager", StorageNetworkManagerImpl.class);
addManager("ExternalLoadBalancerDeviceManager", ExternalLoadBalancerDeviceManagerImpl.class);
addManager("ExternalLoadBalancerUsageManager", ExternalLoadBalancerUsageManagerImpl.class);
addManager("HA Manager", HighAvailabilityManagerImpl.class);
}

View File

@ -101,11 +101,4 @@ public interface ExternalLoadBalancerDeviceManager extends Manager{
public boolean manageGuestNetworkWithExternalLoadBalancer(boolean add, Network guestConfig) throws ResourceUnavailableException,
InsufficientCapacityException;
/**
* updates the network usage stats for a LB rule, associated with an external LB device, that is being revoked as part of Delete LB rule or release IP actions
* @param loadBalancerRuleId
*/
public void updateExternalLoadBalancerNetworkUsageStats(long loadBalancerRuleId);
}

View File

@ -25,19 +25,13 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.ejb.Local;
import javax.naming.ConfigurationException;
import org.apache.log4j.Logger;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.ExternalNetworkResourceUsageAnswer;
import com.cloud.agent.api.ExternalNetworkResourceUsageCommand;
import com.cloud.agent.api.StartupCommand;
import com.cloud.agent.api.StartupExternalLoadBalancerCommand;
import com.cloud.agent.api.routing.CreateLoadBalancerApplianceCommand;
@ -108,28 +102,22 @@ import com.cloud.resource.UnableDeleteHostException;
import com.cloud.server.api.response.ExternalLoadBalancerResponse;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.AccountVO;
import com.cloud.user.UserStatisticsVO;
import com.cloud.user.dao.AccountDao;
import com.cloud.user.dao.UserStatisticsDao;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.component.AdapterBase;
import com.cloud.utils.component.Inject;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.GlobalLock;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.exception.ExecutionException;
import com.cloud.utils.net.NetUtils;
import com.cloud.utils.net.UrlUtil;
import com.cloud.vm.DomainRouterVO;
import com.cloud.vm.Nic.ReservationStrategy;
import com.cloud.vm.Nic.State;
import com.cloud.vm.NicVO;
import com.cloud.vm.dao.DomainRouterDao;
import com.cloud.vm.dao.NicDao;
@Local(value = { ExternalLoadBalancerDeviceManager.class })
public abstract class ExternalLoadBalancerDeviceManagerImpl extends AdapterBase implements ExternalLoadBalancerDeviceManager, ResourceStateAdapter {
@Inject
@ -189,8 +177,6 @@ public abstract class ExternalLoadBalancerDeviceManagerImpl extends AdapterBase
@Inject
protected HostPodDao _podDao = null;
ScheduledExecutorService _executor;
private int _externalNetworkStatsInterval;
private long _defaultLbCapacity;
private static final org.apache.log4j.Logger s_logger = Logger.getLogger(ExternalLoadBalancerDeviceManagerImpl.class);
@ -1005,11 +991,6 @@ public abstract class ExternalLoadBalancerDeviceManagerImpl extends AdapterBase
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
super.configure(name, params);
_externalNetworkStatsInterval = NumbersUtil.parseInt(_configDao.getValue(Config.ExternalNetworkStatsInterval.key()), 300);
if (_externalNetworkStatsInterval > 0) {
_executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("ExternalNetworkMonitor"));
}
_defaultLbCapacity = NumbersUtil.parseLong(_configDao.getValue(Config.DefaultExternalLoadBalancerCapacity.key()), 50);
_resourceMgr.registerResourceStateAdapter(this.getClass().getSimpleName(), this);
return true;
@ -1017,9 +998,6 @@ public abstract class ExternalLoadBalancerDeviceManagerImpl extends AdapterBase
@Override
public boolean start() {
if (_externalNetworkStatsInterval > 0) {
_executor.scheduleAtFixedRate(new ExternalLoadBalancerDeviceNetworkUsageTask(), _externalNetworkStatsInterval, _externalNetworkStatsInterval, TimeUnit.SECONDS);
}
return true;
}
@ -1028,382 +1006,6 @@ public abstract class ExternalLoadBalancerDeviceManagerImpl extends AdapterBase
return true;
}
protected class ExternalLoadBalancerDeviceNetworkUsageTask implements Runnable {
public ExternalLoadBalancerDeviceNetworkUsageTask() {
}
@Override
public void run() {
GlobalLock scanLock = GlobalLock.getInternLock("ExternalLoadBalancerDeviceManagerImpl");
try {
if (scanLock.lock(20)) {
try {
runExternalLoadBalancerNetworkUsageTask();
} finally {
scanLock.unlock();
}
}
} catch (Exception e) {
s_logger.warn("Problems while getting external load balancer device usage", e);
} finally {
scanLock.releaseRef();
}
}
private void runExternalLoadBalancerNetworkUsageTask() {
s_logger.debug("External load balancer devices stats collector is running...");
for (DataCenterVO zone : _dcDao.listAll()) {
List<DomainRouterVO> domainRoutersInZone = _routerDao.listByDataCenter(zone.getId());
if (domainRoutersInZone == null) {
continue;
}
Map<Long, ExternalNetworkResourceUsageAnswer> lbDeviceUsageAnswerMap = new HashMap<Long, ExternalNetworkResourceUsageAnswer>();
List<Long> accountsProcessed = new ArrayList<Long>();
for (DomainRouterVO domainRouter : domainRoutersInZone) {
long accountId = domainRouter.getAccountId();
if (accountsProcessed.contains(new Long(accountId))) {
if (s_logger.isTraceEnabled()) {
s_logger.trace("Networks for Account " + accountId + " are already processed for external network usage, so skipping usage check.");
}
continue;
}
long zoneId = zone.getId();
List<NetworkVO> networksForAccount = _networkDao.listBy(accountId, zoneId, Network.GuestType.Isolated);
if (networksForAccount == null) {
continue;
}
for (NetworkVO network : networksForAccount) {
if (!_networkMgr.networkIsConfiguredForExternalNetworking(zoneId, network.getId())) {
s_logger.debug("Network " + network.getId() + " is not configured for external networking, so skipping usage check.");
continue;
}
ExternalLoadBalancerDeviceVO lbDeviceVO = getExternalLoadBalancerForNetwork(network);
if (lbDeviceVO == null) {
continue;
}
// Get network stats from the external load balancer
ExternalNetworkResourceUsageAnswer lbAnswer = null;
HostVO externalLoadBalancer = _hostDao.findById(lbDeviceVO.getHostId());
if (externalLoadBalancer != null) {
Long lbDeviceId = new Long(externalLoadBalancer.getId());
if (!lbDeviceUsageAnswerMap.containsKey(lbDeviceId)) {
ExternalNetworkResourceUsageCommand cmd = new ExternalNetworkResourceUsageCommand();
lbAnswer = (ExternalNetworkResourceUsageAnswer) _agentMgr.easySend(externalLoadBalancer.getId(), cmd);
if (lbAnswer == null || !lbAnswer.getResult()) {
String details = (lbAnswer != null) ? lbAnswer.getDetails() : "details unavailable";
String msg = "Unable to get external load balancer stats for " + zone.getName() + " due to: " + details + ".";
s_logger.error(msg);
continue;
}
lbDeviceUsageAnswerMap.put(lbDeviceId, lbAnswer);
} else {
if (s_logger.isTraceEnabled()) {
s_logger.trace("Reusing usage Answer for device id " + lbDeviceId + "for Network " + network.getId());
}
lbAnswer = lbDeviceUsageAnswerMap.get(lbDeviceId);
}
}
AccountVO account = _accountDao.findById(accountId);
if (account == null) {
s_logger.debug("Skipping stats update for account with ID " + accountId);
continue;
}
if (!manageStatsEntries(true, accountId, zoneId, network, externalLoadBalancer, lbAnswer)) {
continue;
}
manageStatsEntries(false, accountId, zoneId, network, externalLoadBalancer, lbAnswer);
}
accountsProcessed.add(new Long(accountId));
}
}
}
private boolean updateBytes(UserStatisticsVO userStats, long newCurrentBytesSent, long newCurrentBytesReceived) {
long oldNetBytesSent = userStats.getNetBytesSent();
long oldNetBytesReceived = userStats.getNetBytesReceived();
long oldCurrentBytesSent = userStats.getCurrentBytesSent();
long oldCurrentBytesReceived = userStats.getCurrentBytesReceived();
String warning = "Received an external network stats byte count that was less than the stored value. Zone ID: " + userStats.getDataCenterId() + ", account ID: " + userStats.getAccountId() + ".";
userStats.setCurrentBytesSent(newCurrentBytesSent);
if (oldCurrentBytesSent > newCurrentBytesSent) {
s_logger.warn(warning + "Stored bytes sent: " + oldCurrentBytesSent + ", new bytes sent: " + newCurrentBytesSent + ".");
userStats.setNetBytesSent(oldNetBytesSent + oldCurrentBytesSent);
}
userStats.setCurrentBytesReceived(newCurrentBytesReceived);
if (oldCurrentBytesReceived > newCurrentBytesReceived) {
s_logger.warn(warning + "Stored bytes received: " + oldCurrentBytesReceived + ", new bytes received: " + newCurrentBytesReceived + ".");
userStats.setNetBytesReceived(oldNetBytesReceived + oldCurrentBytesReceived);
}
return _userStatsDao.update(userStats.getId(), userStats);
}
// Creates a new stats entry for the specified parameters, if one doesn't already exist.
private boolean createStatsEntry(long accountId, long zoneId, long networkId, String publicIp, long hostId) {
HostVO host = _hostDao.findById(hostId);
UserStatisticsVO userStats = _userStatsDao.findBy(accountId, zoneId, networkId, publicIp, hostId, host.getType().toString());
if (userStats == null) {
return (_userStatsDao.persist(new UserStatisticsVO(accountId, zoneId, publicIp, hostId, host.getType().toString(), networkId)) != null);
} else {
return true;
}
}
// Updates an existing stats entry with new data from the specified usage answer.
private boolean updateStatsEntry(long accountId, long zoneId, long networkId, String publicIp, long hostId, ExternalNetworkResourceUsageAnswer answer) {
AccountVO account = _accountDao.findById(accountId);
DataCenterVO zone = _dcDao.findById(zoneId);
NetworkVO network = _networkDao.findById(networkId);
HostVO host = _hostDao.findById(hostId);
String statsEntryIdentifier = "account " + account.getAccountName() + ", zone " + zone.getName() + ", network ID " + networkId + ", host ID " + host.getName();
long newCurrentBytesSent = 0;
long newCurrentBytesReceived = 0;
if (publicIp != null) {
long[] bytesSentAndReceived = null;
statsEntryIdentifier += ", public IP: " + publicIp;
if (host.getType().equals(Host.Type.ExternalLoadBalancer) && externalLoadBalancerIsInline(host)) {
// Look up stats for the guest IP address that's mapped to the public IP address
InlineLoadBalancerNicMapVO mapping = _inlineLoadBalancerNicMapDao.findByPublicIpAddress(publicIp);
if (mapping != null) {
NicVO nic = _nicDao.findById(mapping.getNicId());
String loadBalancingIpAddress = nic.getIp4Address();
bytesSentAndReceived = answer.ipBytes.get(loadBalancingIpAddress);
if (bytesSentAndReceived != null) {
bytesSentAndReceived[0] = 0;
}
}
} else {
bytesSentAndReceived = answer.ipBytes.get(publicIp);
}
if (bytesSentAndReceived == null) {
s_logger.debug("Didn't get an external network usage answer for public IP " + publicIp);
} else {
newCurrentBytesSent += bytesSentAndReceived[0];
newCurrentBytesReceived += bytesSentAndReceived[1];
}
} else {
URI broadcastURI = network.getBroadcastUri();
if (broadcastURI == null) {
s_logger.debug("Not updating stats for guest network with ID " + network.getId() + " because the network is not implemented.");
return true;
} else {
long vlanTag = Integer.parseInt(broadcastURI.getHost());
long[] bytesSentAndReceived = answer.guestVlanBytes.get(String.valueOf(vlanTag));
if (bytesSentAndReceived == null) {
s_logger.warn("Didn't get an external network usage answer for guest VLAN " + vlanTag);
} else {
newCurrentBytesSent += bytesSentAndReceived[0];
newCurrentBytesReceived += bytesSentAndReceived[1];
}
}
}
UserStatisticsVO userStats;
try {
userStats = _userStatsDao.lock(accountId, zoneId, networkId, publicIp, hostId, host.getType().toString());
} catch (Exception e) {
s_logger.warn("Unable to find user stats entry for " + statsEntryIdentifier);
return false;
}
if (updateBytes(userStats, newCurrentBytesSent, newCurrentBytesReceived)) {
s_logger.debug("Successfully updated stats for " + statsEntryIdentifier);
return true;
} else {
s_logger.debug("Failed to update stats for " + statsEntryIdentifier);
return false;
}
}
private boolean createOrUpdateStatsEntry(boolean create, long accountId, long zoneId, long networkId, String publicIp, long hostId, ExternalNetworkResourceUsageAnswer answer) {
if (create) {
return createStatsEntry(accountId, zoneId, networkId, publicIp, hostId);
} else {
return updateStatsEntry(accountId, zoneId, networkId, publicIp, hostId, answer);
}
}
/*
* Creates/updates all necessary stats entries for an account and zone.
* Stats entries are created for source NAT IP addresses, static NAT rules, port forwarding rules, and load
* balancing rules
*/
private boolean manageStatsEntries(boolean create, long accountId, long zoneId, Network network,
HostVO externalLoadBalancer, ExternalNetworkResourceUsageAnswer lbAnswer) {
String accountErrorMsg = "Failed to update external network stats entry. Details: account ID = " + accountId;
Transaction txn = Transaction.open(Transaction.CLOUD_DB);
try {
txn.start();
String networkErrorMsg = accountErrorMsg + ", network ID = " + network.getId();
// If an external load balancer is added, manage one entry for each load balancing rule in this network
if (externalLoadBalancer != null && lbAnswer != null) {
List<LoadBalancerVO> loadBalancers = _loadBalancerDao.listByNetworkId(network.getId());
for (LoadBalancerVO loadBalancer : loadBalancers) {
String publicIp = _networkMgr.getIp(loadBalancer.getSourceIpAddressId()).getAddress().addr();
if (!createOrUpdateStatsEntry(create, accountId, zoneId, network.getId(), publicIp, externalLoadBalancer.getId(), lbAnswer)) {
throw new ExecutionException(networkErrorMsg + ", load balancing rule public IP = " + publicIp);
}
}
}
return txn.commit();
} catch (Exception e) {
s_logger.warn("Exception: ", e);
txn.rollback();
return false;
} finally {
txn.close();
}
}
}
@Override
public void updateExternalLoadBalancerNetworkUsageStats(long loadBalancerRuleId){
LoadBalancerVO lb = _loadBalancerDao.findById(loadBalancerRuleId);
if(lb == null){
if(s_logger.isDebugEnabled()){
s_logger.debug("Cannot update usage stats, LB rule is not found");
}
return;
}
long networkId = lb.getNetworkId();
Network network = _networkDao.findById(networkId);
if(network == null){
if(s_logger.isDebugEnabled()){
s_logger.debug("Cannot update usage stats, Network is not found");
}
return;
}
ExternalLoadBalancerDeviceVO lbDeviceVO = getExternalLoadBalancerForNetwork(network);
if (lbDeviceVO == null) {
if(s_logger.isDebugEnabled()){
s_logger.debug("Cannot update usage stats, No external LB device found");
}
return;
}
// Get network stats from the external load balancer
ExternalNetworkResourceUsageAnswer lbAnswer = null;
HostVO externalLoadBalancer = _hostDao.findById(lbDeviceVO.getHostId());
if (externalLoadBalancer != null) {
ExternalNetworkResourceUsageCommand cmd = new ExternalNetworkResourceUsageCommand();
lbAnswer = (ExternalNetworkResourceUsageAnswer) _agentMgr.easySend(externalLoadBalancer.getId(), cmd);
if (lbAnswer == null || !lbAnswer.getResult()) {
String details = (lbAnswer != null) ? lbAnswer.getDetails() : "details unavailable";
String msg = "Unable to get external load balancer stats for network" + networkId + " due to: " + details + ".";
s_logger.error(msg);
return;
}
}
long accountId = lb.getAccountId();
AccountVO account = _accountDao.findById(accountId);
if (account == null) {
s_logger.debug("Skipping stats update for external LB for account with ID " + accountId);
return;
}
String publicIp = _networkMgr.getIp(lb.getSourceIpAddressId()).getAddress().addr();
DataCenterVO zone = _dcDao.findById(network.getDataCenterId());
String statsEntryIdentifier = "account " + account.getAccountName() + ", zone " + zone.getName() + ", network ID " + networkId + ", host ID " + externalLoadBalancer.getName();
long newCurrentBytesSent = 0;
long newCurrentBytesReceived = 0;
if (publicIp != null) {
long[] bytesSentAndReceived = null;
statsEntryIdentifier += ", public IP: " + publicIp;
if (externalLoadBalancer.getType().equals(Host.Type.ExternalLoadBalancer) && externalLoadBalancerIsInline(externalLoadBalancer)) {
// Look up stats for the guest IP address that's mapped to the public IP address
InlineLoadBalancerNicMapVO mapping = _inlineLoadBalancerNicMapDao.findByPublicIpAddress(publicIp);
if (mapping != null) {
NicVO nic = _nicDao.findById(mapping.getNicId());
String loadBalancingIpAddress = nic.getIp4Address();
bytesSentAndReceived = lbAnswer.ipBytes.get(loadBalancingIpAddress);
if (bytesSentAndReceived != null) {
bytesSentAndReceived[0] = 0;
}
}
} else {
bytesSentAndReceived = lbAnswer.ipBytes.get(publicIp);
}
if (bytesSentAndReceived == null) {
s_logger.debug("Didn't get an external network usage answer for public IP " + publicIp);
} else {
newCurrentBytesSent += bytesSentAndReceived[0];
newCurrentBytesReceived += bytesSentAndReceived[1];
}
UserStatisticsVO userStats;
final Transaction txn = Transaction.currentTxn();
try {
txn.start();
userStats = _userStatsDao.lock(accountId, zone.getId(), networkId, publicIp, externalLoadBalancer.getId(), externalLoadBalancer.getType().toString());
if(userStats != null){
long oldNetBytesSent = userStats.getNetBytesSent();
long oldNetBytesReceived = userStats.getNetBytesReceived();
long oldCurrentBytesSent = userStats.getCurrentBytesSent();
long oldCurrentBytesReceived = userStats.getCurrentBytesReceived();
String warning = "Received an external network stats byte count that was less than the stored value. Zone ID: " + userStats.getDataCenterId() + ", account ID: " + userStats.getAccountId() + ".";
userStats.setCurrentBytesSent(newCurrentBytesSent);
if (oldCurrentBytesSent > newCurrentBytesSent) {
s_logger.warn(warning + "Stored bytes sent: " + oldCurrentBytesSent + ", new bytes sent: " + newCurrentBytesSent + ".");
userStats.setNetBytesSent(oldNetBytesSent + oldCurrentBytesSent);
}
userStats.setCurrentBytesReceived(newCurrentBytesReceived);
if (oldCurrentBytesReceived > newCurrentBytesReceived) {
s_logger.warn(warning + "Stored bytes received: " + oldCurrentBytesReceived + ", new bytes received: " + newCurrentBytesReceived + ".");
userStats.setNetBytesReceived(oldNetBytesReceived + oldCurrentBytesReceived);
}
if (_userStatsDao.update(userStats.getId(), userStats)) {
s_logger.debug("Successfully updated stats for " + statsEntryIdentifier);
} else {
s_logger.debug("Failed to update stats for " + statsEntryIdentifier);
}
}else {
s_logger.warn("Unable to find user stats entry for " + statsEntryIdentifier);
}
txn.commit();
}catch (final Exception e) {
txn.rollback();
throw new CloudRuntimeException("Problem getting stats after reboot/stop ", e);
}
}
}
@Override
public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) {
// TODO Auto-generated method stub

View File

@ -0,0 +1,36 @@
/**
* * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved
*
*
* This software is licensed under the GNU General Public License v3 or later.
*
* It is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.cloud.network;
import com.cloud.utils.component.Manager;
/* ExternalLoadBalancerUsageManager implements a periodic task that retrieves and updates the network usage stats from all external load balancer devices.
*/
public interface ExternalLoadBalancerUsageManager extends Manager{
/**
* updates the network usage stats for a LB rule, associated with an external LB device, that is being revoked as part of Delete LB rule or release IP actions
* @param loadBalancerRuleId
*/
public void updateExternalLoadBalancerNetworkUsageStats(long loadBalancerRuleId);
}

View File

@ -0,0 +1,546 @@
package com.cloud.network;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.ejb.Local;
import javax.naming.ConfigurationException;
import org.apache.log4j.Logger;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.ExternalNetworkResourceUsageAnswer;
import com.cloud.agent.api.ExternalNetworkResourceUsageCommand;
import com.cloud.configuration.Config;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.dc.dao.HostPodDao;
import com.cloud.dc.dao.VlanDao;
import com.cloud.host.DetailVO;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.host.dao.HostDetailsDao;
import com.cloud.network.dao.ExternalFirewallDeviceDao;
import com.cloud.network.dao.ExternalLoadBalancerDeviceDao;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.InlineLoadBalancerNicMapDao;
import com.cloud.network.dao.LoadBalancerDao;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkExternalFirewallDao;
import com.cloud.network.dao.NetworkExternalLoadBalancerDao;
import com.cloud.network.dao.NetworkServiceMapDao;
import com.cloud.network.dao.PhysicalNetworkDao;
import com.cloud.network.dao.PhysicalNetworkServiceProviderDao;
import com.cloud.network.rules.dao.PortForwardingRulesDao;
import com.cloud.offerings.dao.NetworkOfferingDao;
import com.cloud.resource.ResourceManager;
import com.cloud.user.AccountManager;
import com.cloud.user.AccountVO;
import com.cloud.user.UserStatisticsVO;
import com.cloud.user.dao.AccountDao;
import com.cloud.user.dao.UserStatisticsDao;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.component.Inject;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.db.GlobalLock;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.exception.ExecutionException;
import com.cloud.vm.DomainRouterVO;
import com.cloud.vm.NicVO;
import com.cloud.vm.dao.DomainRouterDao;
import com.cloud.vm.dao.NicDao;
@Local(value = { ExternalLoadBalancerUsageManager.class })
public class ExternalLoadBalancerUsageManagerImpl implements ExternalLoadBalancerUsageManager {
String _name;
@Inject
NetworkExternalLoadBalancerDao _networkExternalLBDao;
@Inject
ExternalLoadBalancerDeviceDao _externalLoadBalancerDeviceDao;
@Inject
HostDao _hostDao;
@Inject
DataCenterDao _dcDao;
@Inject
NetworkManager _networkMgr;
@Inject
InlineLoadBalancerNicMapDao _inlineLoadBalancerNicMapDao;
@Inject
NicDao _nicDao;
@Inject
AgentManager _agentMgr;
@Inject
ResourceManager _resourceMgr;
@Inject
IPAddressDao _ipAddressDao;
@Inject
VlanDao _vlanDao;
@Inject
NetworkOfferingDao _networkOfferingDao;
@Inject
AccountDao _accountDao;
@Inject
PhysicalNetworkDao _physicalNetworkDao;
@Inject
PhysicalNetworkServiceProviderDao _physicalNetworkServiceProviderDao;
@Inject
AccountManager _accountMgr;
@Inject
UserStatisticsDao _userStatsDao;
@Inject
NetworkDao _networkDao;
@Inject
DomainRouterDao _routerDao;
@Inject
LoadBalancerDao _loadBalancerDao;
@Inject
PortForwardingRulesDao _portForwardingRulesDao;
@Inject
ConfigurationDao _configDao;
@Inject
HostDetailsDao _hostDetailDao;
@Inject
NetworkExternalLoadBalancerDao _networkLBDao;
@Inject
NetworkServiceMapDao _ntwkSrvcProviderDao;
@Inject
NetworkExternalFirewallDao _networkExternalFirewallDao;
@Inject
ExternalFirewallDeviceDao _externalFirewallDeviceDao;
@Inject
protected HostPodDao _podDao = null;
ScheduledExecutorService _executor;
private int _externalNetworkStatsInterval;
private static final org.apache.log4j.Logger s_logger = Logger.getLogger(ExternalLoadBalancerUsageManagerImpl.class);
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
_externalNetworkStatsInterval = NumbersUtil.parseInt(_configDao.getValue(Config.ExternalNetworkStatsInterval.key()), 300);
if (_externalNetworkStatsInterval > 0) {
_executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("ExternalNetworkMonitor"));
}
return true;
}
@Override
public boolean start() {
if (_externalNetworkStatsInterval > 0) {
_executor.scheduleAtFixedRate(new ExternalLoadBalancerDeviceNetworkUsageTask(), _externalNetworkStatsInterval, _externalNetworkStatsInterval, TimeUnit.SECONDS);
}
return true;
}
@Override
public boolean stop() {
return true;
}
@Override
public String getName() {
return _name;
}
private ExternalLoadBalancerDeviceVO getExternalLoadBalancerForNetwork(Network network) {
NetworkExternalLoadBalancerVO lbDeviceForNetwork = _networkExternalLBDao.findByNetworkId(network.getId());
if (lbDeviceForNetwork != null) {
long lbDeviceId = lbDeviceForNetwork.getExternalLBDeviceId();
ExternalLoadBalancerDeviceVO lbDeviceVo = _externalLoadBalancerDeviceDao.findById(lbDeviceId);
assert (lbDeviceVo != null);
return lbDeviceVo;
}
return null;
}
private boolean externalLoadBalancerIsInline(HostVO externalLoadBalancer) {
DetailVO detail = _hostDetailDao.findDetail(externalLoadBalancer.getId(), "inline");
return (detail != null && detail.getValue().equals("true"));
}
@Override
public void updateExternalLoadBalancerNetworkUsageStats(long loadBalancerRuleId){
LoadBalancerVO lb = _loadBalancerDao.findById(loadBalancerRuleId);
if(lb == null){
if(s_logger.isDebugEnabled()){
s_logger.debug("Cannot update usage stats, LB rule is not found");
}
return;
}
long networkId = lb.getNetworkId();
Network network = _networkDao.findById(networkId);
if(network == null){
if(s_logger.isDebugEnabled()){
s_logger.debug("Cannot update usage stats, Network is not found");
}
return;
}
ExternalLoadBalancerDeviceVO lbDeviceVO = getExternalLoadBalancerForNetwork(network);
if (lbDeviceVO == null) {
if(s_logger.isDebugEnabled()){
s_logger.debug("Cannot update usage stats, No external LB device found");
}
return;
}
// Get network stats from the external load balancer
ExternalNetworkResourceUsageAnswer lbAnswer = null;
HostVO externalLoadBalancer = _hostDao.findById(lbDeviceVO.getHostId());
if (externalLoadBalancer != null) {
ExternalNetworkResourceUsageCommand cmd = new ExternalNetworkResourceUsageCommand();
lbAnswer = (ExternalNetworkResourceUsageAnswer) _agentMgr.easySend(externalLoadBalancer.getId(), cmd);
if (lbAnswer == null || !lbAnswer.getResult()) {
String details = (lbAnswer != null) ? lbAnswer.getDetails() : "details unavailable";
String msg = "Unable to get external load balancer stats for network" + networkId + " due to: " + details + ".";
s_logger.error(msg);
return;
}
}
long accountId = lb.getAccountId();
AccountVO account = _accountDao.findById(accountId);
if (account == null) {
s_logger.debug("Skipping stats update for external LB for account with ID " + accountId);
return;
}
String publicIp = _networkMgr.getIp(lb.getSourceIpAddressId()).getAddress().addr();
DataCenterVO zone = _dcDao.findById(network.getDataCenterId());
String statsEntryIdentifier = "account " + account.getAccountName() + ", zone " + zone.getName() + ", network ID " + networkId + ", host ID " + externalLoadBalancer.getName();
long newCurrentBytesSent = 0;
long newCurrentBytesReceived = 0;
if (publicIp != null) {
long[] bytesSentAndReceived = null;
statsEntryIdentifier += ", public IP: " + publicIp;
if (externalLoadBalancer.getType().equals(Host.Type.ExternalLoadBalancer) && externalLoadBalancerIsInline(externalLoadBalancer)) {
// Look up stats for the guest IP address that's mapped to the public IP address
InlineLoadBalancerNicMapVO mapping = _inlineLoadBalancerNicMapDao.findByPublicIpAddress(publicIp);
if (mapping != null) {
NicVO nic = _nicDao.findById(mapping.getNicId());
String loadBalancingIpAddress = nic.getIp4Address();
bytesSentAndReceived = lbAnswer.ipBytes.get(loadBalancingIpAddress);
if (bytesSentAndReceived != null) {
bytesSentAndReceived[0] = 0;
}
}
} else {
bytesSentAndReceived = lbAnswer.ipBytes.get(publicIp);
}
if (bytesSentAndReceived == null) {
s_logger.debug("Didn't get an external network usage answer for public IP " + publicIp);
} else {
newCurrentBytesSent += bytesSentAndReceived[0];
newCurrentBytesReceived += bytesSentAndReceived[1];
}
UserStatisticsVO userStats;
final Transaction txn = Transaction.currentTxn();
try {
txn.start();
userStats = _userStatsDao.lock(accountId, zone.getId(), networkId, publicIp, externalLoadBalancer.getId(), externalLoadBalancer.getType().toString());
if(userStats != null){
long oldNetBytesSent = userStats.getNetBytesSent();
long oldNetBytesReceived = userStats.getNetBytesReceived();
long oldCurrentBytesSent = userStats.getCurrentBytesSent();
long oldCurrentBytesReceived = userStats.getCurrentBytesReceived();
String warning = "Received an external network stats byte count that was less than the stored value. Zone ID: " + userStats.getDataCenterId() + ", account ID: " + userStats.getAccountId() + ".";
userStats.setCurrentBytesSent(newCurrentBytesSent);
if (oldCurrentBytesSent > newCurrentBytesSent) {
s_logger.warn(warning + "Stored bytes sent: " + oldCurrentBytesSent + ", new bytes sent: " + newCurrentBytesSent + ".");
userStats.setNetBytesSent(oldNetBytesSent + oldCurrentBytesSent);
}
userStats.setCurrentBytesReceived(newCurrentBytesReceived);
if (oldCurrentBytesReceived > newCurrentBytesReceived) {
s_logger.warn(warning + "Stored bytes received: " + oldCurrentBytesReceived + ", new bytes received: " + newCurrentBytesReceived + ".");
userStats.setNetBytesReceived(oldNetBytesReceived + oldCurrentBytesReceived);
}
if (_userStatsDao.update(userStats.getId(), userStats)) {
s_logger.debug("Successfully updated stats for " + statsEntryIdentifier);
} else {
s_logger.debug("Failed to update stats for " + statsEntryIdentifier);
}
}else {
s_logger.warn("Unable to find user stats entry for " + statsEntryIdentifier);
}
txn.commit();
}catch (final Exception e) {
txn.rollback();
throw new CloudRuntimeException("Problem getting stats after reboot/stop ", e);
}
}
}
protected class ExternalLoadBalancerDeviceNetworkUsageTask implements Runnable {
public ExternalLoadBalancerDeviceNetworkUsageTask() {
}
@Override
public void run() {
GlobalLock scanLock = GlobalLock.getInternLock("ExternalLoadBalancerUsageManagerImpl");
try {
if (scanLock.lock(20)) {
try {
runExternalLoadBalancerNetworkUsageTask();
} finally {
scanLock.unlock();
}
}
} catch (Exception e) {
s_logger.warn("Problems while getting external load balancer device usage", e);
} finally {
scanLock.releaseRef();
}
}
private void runExternalLoadBalancerNetworkUsageTask() {
s_logger.debug("External load balancer devices stats collector is running...");
for (DataCenterVO zone : _dcDao.listAll()) {
List<DomainRouterVO> domainRoutersInZone = _routerDao.listByDataCenter(zone.getId());
if (domainRoutersInZone == null) {
continue;
}
Map<Long, ExternalNetworkResourceUsageAnswer> lbDeviceUsageAnswerMap = new HashMap<Long, ExternalNetworkResourceUsageAnswer>();
List<Long> accountsProcessed = new ArrayList<Long>();
for (DomainRouterVO domainRouter : domainRoutersInZone) {
long accountId = domainRouter.getAccountId();
if (accountsProcessed.contains(new Long(accountId))) {
if (s_logger.isTraceEnabled()) {
s_logger.trace("Networks for Account " + accountId + " are already processed for external network usage, so skipping usage check.");
}
continue;
}
long zoneId = zone.getId();
List<NetworkVO> networksForAccount = _networkDao.listBy(accountId, zoneId, Network.GuestType.Isolated);
if (networksForAccount == null) {
continue;
}
for (NetworkVO network : networksForAccount) {
if (!_networkMgr.networkIsConfiguredForExternalNetworking(zoneId, network.getId())) {
s_logger.debug("Network " + network.getId() + " is not configured for external networking, so skipping usage check.");
continue;
}
ExternalLoadBalancerDeviceVO lbDeviceVO = getExternalLoadBalancerForNetwork(network);
if (lbDeviceVO == null) {
continue;
}
// Get network stats from the external load balancer
ExternalNetworkResourceUsageAnswer lbAnswer = null;
HostVO externalLoadBalancer = _hostDao.findById(lbDeviceVO.getHostId());
if (externalLoadBalancer != null) {
Long lbDeviceId = new Long(externalLoadBalancer.getId());
if (!lbDeviceUsageAnswerMap.containsKey(lbDeviceId)) {
ExternalNetworkResourceUsageCommand cmd = new ExternalNetworkResourceUsageCommand();
lbAnswer = (ExternalNetworkResourceUsageAnswer) _agentMgr.easySend(externalLoadBalancer.getId(), cmd);
if (lbAnswer == null || !lbAnswer.getResult()) {
String details = (lbAnswer != null) ? lbAnswer.getDetails() : "details unavailable";
String msg = "Unable to get external load balancer stats for " + zone.getName() + " due to: " + details + ".";
s_logger.error(msg);
continue;
}
lbDeviceUsageAnswerMap.put(lbDeviceId, lbAnswer);
} else {
if (s_logger.isTraceEnabled()) {
s_logger.trace("Reusing usage Answer for device id " + lbDeviceId + "for Network " + network.getId());
}
lbAnswer = lbDeviceUsageAnswerMap.get(lbDeviceId);
}
}
AccountVO account = _accountDao.findById(accountId);
if (account == null) {
s_logger.debug("Skipping stats update for account with ID " + accountId);
continue;
}
if (!manageStatsEntries(true, accountId, zoneId, network, externalLoadBalancer, lbAnswer)) {
continue;
}
manageStatsEntries(false, accountId, zoneId, network, externalLoadBalancer, lbAnswer);
}
accountsProcessed.add(new Long(accountId));
}
}
}
private boolean updateBytes(UserStatisticsVO userStats, long newCurrentBytesSent, long newCurrentBytesReceived) {
long oldNetBytesSent = userStats.getNetBytesSent();
long oldNetBytesReceived = userStats.getNetBytesReceived();
long oldCurrentBytesSent = userStats.getCurrentBytesSent();
long oldCurrentBytesReceived = userStats.getCurrentBytesReceived();
String warning = "Received an external network stats byte count that was less than the stored value. Zone ID: " + userStats.getDataCenterId() + ", account ID: " + userStats.getAccountId() + ".";
userStats.setCurrentBytesSent(newCurrentBytesSent);
if (oldCurrentBytesSent > newCurrentBytesSent) {
s_logger.warn(warning + "Stored bytes sent: " + oldCurrentBytesSent + ", new bytes sent: " + newCurrentBytesSent + ".");
userStats.setNetBytesSent(oldNetBytesSent + oldCurrentBytesSent);
}
userStats.setCurrentBytesReceived(newCurrentBytesReceived);
if (oldCurrentBytesReceived > newCurrentBytesReceived) {
s_logger.warn(warning + "Stored bytes received: " + oldCurrentBytesReceived + ", new bytes received: " + newCurrentBytesReceived + ".");
userStats.setNetBytesReceived(oldNetBytesReceived + oldCurrentBytesReceived);
}
return _userStatsDao.update(userStats.getId(), userStats);
}
// Creates a new stats entry for the specified parameters, if one doesn't already exist.
private boolean createStatsEntry(long accountId, long zoneId, long networkId, String publicIp, long hostId) {
HostVO host = _hostDao.findById(hostId);
UserStatisticsVO userStats = _userStatsDao.findBy(accountId, zoneId, networkId, publicIp, hostId, host.getType().toString());
if (userStats == null) {
return (_userStatsDao.persist(new UserStatisticsVO(accountId, zoneId, publicIp, hostId, host.getType().toString(), networkId)) != null);
} else {
return true;
}
}
// Updates an existing stats entry with new data from the specified usage answer.
private boolean updateStatsEntry(long accountId, long zoneId, long networkId, String publicIp, long hostId, ExternalNetworkResourceUsageAnswer answer) {
AccountVO account = _accountDao.findById(accountId);
DataCenterVO zone = _dcDao.findById(zoneId);
NetworkVO network = _networkDao.findById(networkId);
HostVO host = _hostDao.findById(hostId);
String statsEntryIdentifier = "account " + account.getAccountName() + ", zone " + zone.getName() + ", network ID " + networkId + ", host ID " + host.getName();
long newCurrentBytesSent = 0;
long newCurrentBytesReceived = 0;
if (publicIp != null) {
long[] bytesSentAndReceived = null;
statsEntryIdentifier += ", public IP: " + publicIp;
if (host.getType().equals(Host.Type.ExternalLoadBalancer) && externalLoadBalancerIsInline(host)) {
// Look up stats for the guest IP address that's mapped to the public IP address
InlineLoadBalancerNicMapVO mapping = _inlineLoadBalancerNicMapDao.findByPublicIpAddress(publicIp);
if (mapping != null) {
NicVO nic = _nicDao.findById(mapping.getNicId());
String loadBalancingIpAddress = nic.getIp4Address();
bytesSentAndReceived = answer.ipBytes.get(loadBalancingIpAddress);
if (bytesSentAndReceived != null) {
bytesSentAndReceived[0] = 0;
}
}
} else {
bytesSentAndReceived = answer.ipBytes.get(publicIp);
}
if (bytesSentAndReceived == null) {
s_logger.debug("Didn't get an external network usage answer for public IP " + publicIp);
} else {
newCurrentBytesSent += bytesSentAndReceived[0];
newCurrentBytesReceived += bytesSentAndReceived[1];
}
} else {
URI broadcastURI = network.getBroadcastUri();
if (broadcastURI == null) {
s_logger.debug("Not updating stats for guest network with ID " + network.getId() + " because the network is not implemented.");
return true;
} else {
long vlanTag = Integer.parseInt(broadcastURI.getHost());
long[] bytesSentAndReceived = answer.guestVlanBytes.get(String.valueOf(vlanTag));
if (bytesSentAndReceived == null) {
s_logger.warn("Didn't get an external network usage answer for guest VLAN " + vlanTag);
} else {
newCurrentBytesSent += bytesSentAndReceived[0];
newCurrentBytesReceived += bytesSentAndReceived[1];
}
}
}
UserStatisticsVO userStats;
try {
userStats = _userStatsDao.lock(accountId, zoneId, networkId, publicIp, hostId, host.getType().toString());
} catch (Exception e) {
s_logger.warn("Unable to find user stats entry for " + statsEntryIdentifier);
return false;
}
if (updateBytes(userStats, newCurrentBytesSent, newCurrentBytesReceived)) {
s_logger.debug("Successfully updated stats for " + statsEntryIdentifier);
return true;
} else {
s_logger.debug("Failed to update stats for " + statsEntryIdentifier);
return false;
}
}
private boolean createOrUpdateStatsEntry(boolean create, long accountId, long zoneId, long networkId, String publicIp, long hostId, ExternalNetworkResourceUsageAnswer answer) {
if (create) {
return createStatsEntry(accountId, zoneId, networkId, publicIp, hostId);
} else {
return updateStatsEntry(accountId, zoneId, networkId, publicIp, hostId, answer);
}
}
/*
* Creates/updates all necessary stats entries for an account and zone.
* Stats entries are created for source NAT IP addresses, static NAT rules, port forwarding rules, and load
* balancing rules
*/
private boolean manageStatsEntries(boolean create, long accountId, long zoneId, Network network,
HostVO externalLoadBalancer, ExternalNetworkResourceUsageAnswer lbAnswer) {
String accountErrorMsg = "Failed to update external network stats entry. Details: account ID = " + accountId;
Transaction txn = Transaction.open(Transaction.CLOUD_DB);
try {
txn.start();
String networkErrorMsg = accountErrorMsg + ", network ID = " + network.getId();
// If an external load balancer is added, manage one entry for each load balancing rule in this network
if (externalLoadBalancer != null && lbAnswer != null) {
List<LoadBalancerVO> loadBalancers = _loadBalancerDao.listByNetworkId(network.getId());
for (LoadBalancerVO loadBalancer : loadBalancers) {
String publicIp = _networkMgr.getIp(loadBalancer.getSourceIpAddressId()).getAddress().addr();
if (!createOrUpdateStatsEntry(create, accountId, zoneId, network.getId(), publicIp, externalLoadBalancer.getId(), lbAnswer)) {
throw new ExecutionException(networkErrorMsg + ", load balancing rule public IP = " + publicIp);
}
}
}
return txn.commit();
} catch (Exception e) {
s_logger.warn("Exception: ", e);
txn.rollback();
return false;
} finally {
txn.close();
}
}
}
}

View File

@ -52,8 +52,8 @@ import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.NetworkRuleConflictException;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.ExternalLoadBalancerDeviceManager;
import com.cloud.network.dao.NetworkServiceMapDao;
import com.cloud.network.ExternalLoadBalancerUsageManager;
import com.cloud.network.IPAddressVO;
import com.cloud.network.IpAddress;
import com.cloud.network.LBStickinessPolicyVO;
@ -161,7 +161,7 @@ public class LoadBalancingRulesManagerImpl<Type> implements LoadBalancingRulesMa
@Inject
ConfigurationManager _configMgr;
@Inject
ExternalLoadBalancerDeviceManager _externalLBMgr;
ExternalLoadBalancerUsageManager _externalLBUsageMgr;
@Inject
NetworkServiceMapDao _ntwkSrvcDao;
@ -637,7 +637,7 @@ public class LoadBalancingRulesManagerImpl<Type> implements LoadBalancingRulesMa
NetworkVO network = _networkDao.findById(lb.getNetworkId());
if (network != null) {
if (_networkMgr.networkIsConfiguredForExternalNetworking(network.getDataCenterId(), network.getId())) {
_externalLBMgr.updateExternalLoadBalancerNetworkUsageStats(loadBalancerId);
_externalLBUsageMgr.updateExternalLoadBalancerNetworkUsageStats(loadBalancerId);
}
}