| // 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.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.inject.Inject; | |
| import javax.naming.ConfigurationException; | |
| import org.apache.cloudstack.framework.config.dao.ConfigurationDao; | |
| import org.apache.cloudstack.managed.context.ManagedContextRunnable; | |
| import org.springframework.stereotype.Component; | |
| 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.dc.DataCenterVO; | |
| import com.cloud.dc.dao.DataCenterDao; | |
| import com.cloud.dc.dao.HostPodDao; | |
| import com.cloud.dc.dao.VlanDao; | |
| import com.cloud.host.Host; | |
| import com.cloud.host.HostVO; | |
| import com.cloud.host.Host.Type; | |
| import com.cloud.host.dao.HostDao; | |
| import com.cloud.host.dao.HostDetailsDao; | |
| import com.cloud.network.Networks.BroadcastDomainType; | |
| import com.cloud.network.dao.ExternalFirewallDeviceDao; | |
| import com.cloud.network.dao.ExternalFirewallDeviceVO; | |
| import com.cloud.network.dao.ExternalLoadBalancerDeviceDao; | |
| import com.cloud.network.dao.ExternalLoadBalancerDeviceVO; | |
| import com.cloud.network.dao.IPAddressDao; | |
| import com.cloud.network.dao.IPAddressVO; | |
| import com.cloud.network.dao.InlineLoadBalancerNicMapDao; | |
| import com.cloud.network.dao.InlineLoadBalancerNicMapVO; | |
| import com.cloud.network.dao.LoadBalancerDao; | |
| import com.cloud.network.dao.LoadBalancerVO; | |
| import com.cloud.network.dao.NetworkDao; | |
| import com.cloud.network.dao.NetworkExternalFirewallDao; | |
| import com.cloud.network.dao.NetworkExternalFirewallVO; | |
| import com.cloud.network.dao.NetworkExternalLoadBalancerDao; | |
| import com.cloud.network.dao.NetworkExternalLoadBalancerVO; | |
| import com.cloud.network.dao.NetworkServiceMapDao; | |
| import com.cloud.network.dao.NetworkVO; | |
| import com.cloud.network.dao.PhysicalNetworkDao; | |
| import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; | |
| import com.cloud.network.rules.LoadBalancerContainer.Scheme; | |
| import com.cloud.network.rules.PortForwardingRuleVO; | |
| import com.cloud.network.rules.dao.PortForwardingRulesDao; | |
| import com.cloud.offering.NetworkOffering; | |
| 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.ManagerBase; | |
| import com.cloud.utils.concurrency.NamedThreadFactory; | |
| import com.cloud.utils.db.GlobalLock; | |
| import com.cloud.utils.db.Transaction; | |
| import com.cloud.utils.db.TransactionCallbackNoReturn; | |
| import com.cloud.utils.db.TransactionStatus; | |
| import com.cloud.utils.exception.CloudRuntimeException; | |
| import com.cloud.vm.DomainRouterVO; | |
| import com.cloud.vm.NicVO; | |
| import com.cloud.vm.dao.DomainRouterDao; | |
| import com.cloud.vm.dao.NicDao; | |
| import static com.cloud.utils.NumbersUtil.toHumanReadableSize; | |
| @Component | |
| public class ExternalDeviceUsageManagerImpl extends ManagerBase implements ExternalDeviceUsageManager { | |
| String _name; | |
| @Inject | |
| NetworkExternalLoadBalancerDao _networkExternalLBDao; | |
| @Inject | |
| ExternalLoadBalancerDeviceDao _externalLoadBalancerDeviceDao; | |
| @Inject | |
| HostDao _hostDao; | |
| @Inject | |
| DataCenterDao _dcDao; | |
| @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; | |
| @Inject | |
| NetworkModel _networkModel; | |
| ScheduledExecutorService _executor; | |
| private int _externalNetworkStatsInterval; | |
| @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 ExternalDeviceNetworkUsageTask(), _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 ExternalFirewallDeviceVO getExternalFirewallForNetwork(Network network) { | |
| NetworkExternalFirewallVO fwDeviceForNetwork = _networkExternalFirewallDao.findByNetworkId(network.getId()); | |
| if (fwDeviceForNetwork != null) { | |
| long fwDeviceId = fwDeviceForNetwork.getExternalFirewallDeviceId(); | |
| ExternalFirewallDeviceVO fwDevice = _externalFirewallDeviceDao.findById(fwDeviceId); | |
| assert (fwDevice != null); | |
| return fwDevice; | |
| } | |
| return null; | |
| } | |
| @Override | |
| public void updateExternalLoadBalancerNetworkUsageStats(long loadBalancerRuleId) { | |
| LoadBalancerVO lb = _loadBalancerDao.findById(loadBalancerRuleId); | |
| if (lb == null) { | |
| if (logger.isDebugEnabled()) { | |
| 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 (logger.isDebugEnabled()) { | |
| logger.debug("Cannot update usage stats, Network is not found"); | |
| } | |
| return; | |
| } | |
| ExternalLoadBalancerDeviceVO lbDeviceVO = getExternalLoadBalancerForNetwork(network); | |
| if (lbDeviceVO == null) { | |
| if (logger.isDebugEnabled()) { | |
| 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 = String.format("Unable to get external load balancer stats for network %s due to: %s.", network, details); | |
| logger.error(msg); | |
| return; | |
| } | |
| } | |
| long accountId = lb.getAccountId(); | |
| AccountVO account = _accountDao.findById(accountId); | |
| if (account == null) { | |
| logger.debug("Skipping stats update for external LB for account with ID " + accountId); | |
| return; | |
| } | |
| String publicIp = _networkModel.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; | |
| boolean inline = _networkModel.isNetworkInlineMode(network); | |
| if (externalLoadBalancer.getType().equals(Host.Type.ExternalLoadBalancer) && inline) { | |
| // 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.getIPv4Address(); | |
| bytesSentAndReceived = lbAnswer.ipBytes.get(loadBalancingIpAddress); | |
| if (bytesSentAndReceived != null) { | |
| bytesSentAndReceived[0] = 0; | |
| } | |
| } | |
| } else { | |
| bytesSentAndReceived = lbAnswer.ipBytes.get(publicIp); | |
| } | |
| if (bytesSentAndReceived == null) { | |
| logger.debug("Didn't get an external network usage answer for public IP " + publicIp); | |
| } else { | |
| newCurrentBytesSent += bytesSentAndReceived[0]; | |
| newCurrentBytesReceived += bytesSentAndReceived[1]; | |
| } | |
| commitStats(networkId, externalLoadBalancer, accountId, publicIp, zone, statsEntryIdentifier, newCurrentBytesSent, newCurrentBytesReceived); | |
| } | |
| } | |
| private void commitStats(final long networkId, final HostVO externalLoadBalancer, final long accountId, final String publicIp, final DataCenterVO zone, | |
| final String statsEntryIdentifier, final long newCurrentBytesSent, final long newCurrentBytesReceived) { | |
| Transaction.execute(new TransactionCallbackNoReturn() { | |
| @Override | |
| public void doInTransactionWithoutResult(TransactionStatus status) { | |
| UserStatisticsVO userStats; | |
| 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) { | |
| logger.warn(warning + "Stored bytes sent: " + toHumanReadableSize(oldCurrentBytesSent) + ", new bytes sent: " + toHumanReadableSize(newCurrentBytesSent) + "."); | |
| userStats.setNetBytesSent(oldNetBytesSent + oldCurrentBytesSent); | |
| } | |
| userStats.setCurrentBytesReceived(newCurrentBytesReceived); | |
| if (oldCurrentBytesReceived > newCurrentBytesReceived) { | |
| logger.warn(warning + "Stored bytes received: " + toHumanReadableSize(oldCurrentBytesReceived) + ", new bytes received: " + toHumanReadableSize(newCurrentBytesReceived) + "."); | |
| userStats.setNetBytesReceived(oldNetBytesReceived + oldCurrentBytesReceived); | |
| } | |
| if (_userStatsDao.update(userStats.getId(), userStats)) { | |
| logger.debug("Successfully updated stats for " + statsEntryIdentifier); | |
| } else { | |
| logger.debug("Failed to update stats for " + statsEntryIdentifier); | |
| } | |
| } else { | |
| logger.warn("Unable to find user stats entry for " + statsEntryIdentifier); | |
| } | |
| } | |
| }); | |
| } | |
| public boolean isNccServiceProvider(Network network) { | |
| NetworkOffering networkOffering = _networkOfferingDao.findById(network.getNetworkOfferingId()); | |
| if(null!= networkOffering && networkOffering.getServicePackage() != null ) { | |
| return true; | |
| } | |
| else { | |
| return false; | |
| } | |
| } | |
| public HostVO getNetScalerControlCenterForNetwork(Network guestConfig) { | |
| long zoneId = guestConfig.getDataCenterId(); | |
| return _hostDao.findByTypeNameAndZoneId(zoneId, "NetscalerControlCenter", Type.NetScalerControlCenter); | |
| } | |
| protected class ExternalDeviceNetworkUsageTask extends ManagedContextRunnable { | |
| public ExternalDeviceNetworkUsageTask() { | |
| } | |
| @Override | |
| protected void runInContext() { | |
| // Check if there are any external devices | |
| // Skip external device usage collection if none exist | |
| if(_hostDao.listByType(Host.Type.ExternalFirewall).isEmpty() && _hostDao.listByType(Host.Type.ExternalLoadBalancer).isEmpty()){ | |
| logger.debug("External devices are not used. Skipping external device usage collection"); | |
| return; | |
| } | |
| GlobalLock scanLock = GlobalLock.getInternLock("ExternalDeviceNetworkUsageManagerImpl"); | |
| try { | |
| if (scanLock.lock(20)) { | |
| try { | |
| runExternalDeviceNetworkUsageTask(); | |
| } finally { | |
| scanLock.unlock(); | |
| } | |
| } | |
| } catch (Exception e) { | |
| logger.warn("Problems while getting external device usage", e); | |
| } finally { | |
| scanLock.releaseRef(); | |
| } | |
| } | |
| protected void runExternalDeviceNetworkUsageTask() { | |
| logger.debug("External 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>(); | |
| Map<Long, ExternalNetworkResourceUsageAnswer> fwDeviceUsageAnswerMap = 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 (logger.isTraceEnabled()) { | |
| 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.listByZoneAndGuestType(accountId, zoneId, Network.GuestType.Isolated, false); | |
| if (networksForAccount == null) { | |
| continue; | |
| } | |
| for (NetworkVO network : networksForAccount) { | |
| if (!_networkModel.networkIsConfiguredForExternalNetworking(zoneId, network.getId())) { | |
| logger.debug("Network {} is not configured for external networking, so skipping usage check.", network); | |
| continue; | |
| } | |
| ExternalLoadBalancerDeviceVO lbDeviceVO = getExternalLoadBalancerForNetwork(network); | |
| HostVO externalNcc = null; | |
| boolean isNccNetwork = isNccServiceProvider(network); | |
| if(isNccNetwork) { | |
| externalNcc = getNetScalerControlCenterForNetwork(network); | |
| } | |
| ExternalFirewallDeviceVO fwDeviceVO = getExternalFirewallForNetwork(network); | |
| if (fwDeviceVO == null) { | |
| continue; | |
| } | |
| if(externalNcc == null && lbDeviceVO == null) { | |
| return; | |
| } | |
| // Get network stats from the external firewall | |
| ExternalNetworkResourceUsageAnswer firewallAnswer = null; | |
| HostVO externalFirewall = null; | |
| if (fwDeviceVO != null) { | |
| externalFirewall = _hostDao.findById(fwDeviceVO.getHostId()); | |
| if (externalFirewall != null) { | |
| Long fwDeviceId = new Long(externalFirewall.getId()); | |
| if (!fwDeviceUsageAnswerMap.containsKey(fwDeviceId)) { | |
| try { | |
| ExternalNetworkResourceUsageCommand cmd = new ExternalNetworkResourceUsageCommand(); | |
| firewallAnswer = (ExternalNetworkResourceUsageAnswer)_agentMgr.easySend(externalFirewall.getId(), cmd); | |
| if (firewallAnswer == null || !firewallAnswer.getResult()) { | |
| String details = (firewallAnswer != null) ? firewallAnswer.getDetails() : "details unavailable"; | |
| String msg = "Unable to get external firewall stats for network" + zone.getName() + " due to: " + details + "."; | |
| logger.error(msg); | |
| } else { | |
| fwDeviceUsageAnswerMap.put(fwDeviceId, firewallAnswer); | |
| } | |
| } catch (Exception e) { | |
| String msg = "Unable to get external firewall stats for network" + zone.getName(); | |
| logger.error(msg, e); | |
| } | |
| } else { | |
| if (logger.isTraceEnabled()) { | |
| logger.trace("Reusing usage Answer for device id {} for Network {}", fwDeviceId, network); | |
| } | |
| firewallAnswer = fwDeviceUsageAnswerMap.get(fwDeviceId); | |
| } | |
| } | |
| } | |
| // Get network stats from the external load balancer | |
| ExternalNetworkResourceUsageAnswer lbAnswer = null; | |
| HostVO externalLoadBalancer = null; | |
| if (lbDeviceVO != null || externalNcc != null) { | |
| if(isNccNetwork) { | |
| externalLoadBalancer = externalNcc; | |
| } else { | |
| externalLoadBalancer = _hostDao.findById(lbDeviceVO.getHostId()); | |
| } | |
| if (externalLoadBalancer != null) { | |
| Long lbDeviceId = new Long(externalLoadBalancer.getId()); | |
| if (!lbDeviceUsageAnswerMap.containsKey(lbDeviceId)) { | |
| try { | |
| ExternalNetworkResourceUsageCommand cmd = new ExternalNetworkResourceUsageCommand(network.getId()); | |
| 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 + "."; | |
| logger.error(msg); | |
| } else { | |
| lbDeviceUsageAnswerMap.put(lbDeviceId, lbAnswer); | |
| } | |
| } catch (Exception e) { | |
| String msg = "Unable to get external load balancer stats for " + zone.getName(); | |
| logger.error(msg, e); | |
| } | |
| } else { | |
| if (logger.isTraceEnabled()) { | |
| logger.trace("Reusing usage Answer for device id {} for Network {}", lbDeviceId, network); | |
| } | |
| lbAnswer = lbDeviceUsageAnswerMap.get(lbDeviceId); | |
| } | |
| } | |
| } | |
| if (firewallAnswer == null && lbAnswer == null) { | |
| continue; | |
| } | |
| AccountVO account = _accountDao.findById(accountId); | |
| if (account == null) { | |
| logger.debug("Skipping stats update for account with ID " + accountId); | |
| continue; | |
| } | |
| if (!manageStatsEntries(true, accountId, zoneId, network, externalFirewall, firewallAnswer, externalLoadBalancer, lbAnswer)) { | |
| continue; | |
| } | |
| manageStatsEntries(false, accountId, zoneId, network, externalFirewall, firewallAnswer, 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) { | |
| logger.warn(warning + "Stored bytes sent: " + toHumanReadableSize(oldCurrentBytesSent) + ", new bytes sent: " + toHumanReadableSize(newCurrentBytesSent) + "."); | |
| userStats.setNetBytesSent(oldNetBytesSent + oldCurrentBytesSent); | |
| } | |
| userStats.setCurrentBytesReceived(newCurrentBytesReceived); | |
| if (oldCurrentBytesReceived > newCurrentBytesReceived) { | |
| logger.warn(warning + "Stored bytes received: " + toHumanReadableSize(oldCurrentBytesReceived) + ", new bytes received: " + toHumanReadableSize(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, | |
| boolean inline) { | |
| 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) && inline) { | |
| // 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.getIPv4Address(); | |
| bytesSentAndReceived = answer.ipBytes.get(loadBalancingIpAddress); | |
| if (bytesSentAndReceived != null) { | |
| bytesSentAndReceived[0] = 0; | |
| } | |
| } | |
| } else { | |
| bytesSentAndReceived = answer.ipBytes.get(publicIp); | |
| } | |
| if (bytesSentAndReceived == null) { | |
| 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) { | |
| logger.debug("Not updating stats for guest network {} because the network is not implemented.", network); | |
| return true; | |
| } else { | |
| long vlanTag = Integer.parseInt(BroadcastDomainType.getValue(broadcastURI)); | |
| long[] bytesSentAndReceived = answer.guestVlanBytes.get(String.valueOf(vlanTag)); | |
| if (bytesSentAndReceived == null) { | |
| 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) { | |
| logger.warn("Unable to find user stats entry for " + statsEntryIdentifier); | |
| return false; | |
| } | |
| if (updateBytes(userStats, newCurrentBytesSent, newCurrentBytesReceived)) { | |
| logger.debug("Successfully updated stats for " + statsEntryIdentifier); | |
| return true; | |
| } else { | |
| 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, boolean inline) { | |
| if (create) { | |
| return createStatsEntry(accountId, zoneId, networkId, publicIp, hostId); | |
| } else { | |
| return updateStatsEntry(accountId, zoneId, networkId, publicIp, hostId, answer, inline); | |
| } | |
| } | |
| /* | |
| * 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(final boolean create, final long accountId, final long zoneId, final Network network, final HostVO externalFirewall, | |
| final ExternalNetworkResourceUsageAnswer firewallAnswer, final HostVO externalLoadBalancer, final ExternalNetworkResourceUsageAnswer lbAnswer) { | |
| final String accountErrorMsg = "Failed to update external network stats entry. Details: account ID = " + accountId; | |
| try { | |
| Transaction.execute(new TransactionCallbackNoReturn() { | |
| @Override | |
| public void doInTransactionWithoutResult(TransactionStatus status) { | |
| String networkErrorMsg = accountErrorMsg + ", network ID = " + network.getId(); | |
| boolean sharedSourceNat = false; | |
| Map<Network.Capability, String> sourceNatCapabilities = _networkModel.getNetworkServiceCapabilities(network.getId(), Network.Service.SourceNat); | |
| if (sourceNatCapabilities != null) { | |
| String supportedSourceNatTypes = sourceNatCapabilities.get(Network.Capability.SupportedSourceNatTypes).toLowerCase(); | |
| if (supportedSourceNatTypes.contains("zone")) { | |
| sharedSourceNat = true; | |
| } | |
| } | |
| if (externalFirewall != null && firewallAnswer != null) { | |
| if (!sharedSourceNat) { | |
| // Manage the entry for this network's source NAT IP address | |
| List<IPAddressVO> sourceNatIps = _ipAddressDao.listByAssociatedNetwork(network.getId(), true); | |
| if (sourceNatIps.size() == 1) { | |
| String publicIp = sourceNatIps.get(0).getAddress().addr(); | |
| if (!createOrUpdateStatsEntry(create, accountId, zoneId, network.getId(), publicIp, externalFirewall.getId(), firewallAnswer, false)) { | |
| throw new CloudRuntimeException(networkErrorMsg + ", source NAT IP = " + publicIp); | |
| } | |
| } | |
| // Manage one entry for each static NAT rule in this network | |
| List<IPAddressVO> staticNatIps = _ipAddressDao.listStaticNatPublicIps(network.getId()); | |
| for (IPAddressVO staticNatIp : staticNatIps) { | |
| String publicIp = staticNatIp.getAddress().addr(); | |
| if (!createOrUpdateStatsEntry(create, accountId, zoneId, network.getId(), publicIp, externalFirewall.getId(), firewallAnswer, false)) { | |
| throw new CloudRuntimeException(networkErrorMsg + ", static NAT rule public IP = " + publicIp); | |
| } | |
| } | |
| // Manage one entry for each port forwarding rule in this network | |
| List<PortForwardingRuleVO> portForwardingRules = _portForwardingRulesDao.listByNetwork(network.getId()); | |
| for (PortForwardingRuleVO portForwardingRule : portForwardingRules) { | |
| String publicIp = _networkModel.getIp(portForwardingRule.getSourceIpAddressId()).getAddress().addr(); | |
| if (!createOrUpdateStatsEntry(create, accountId, zoneId, network.getId(), publicIp, externalFirewall.getId(), firewallAnswer, false)) { | |
| throw new CloudRuntimeException(networkErrorMsg + ", port forwarding rule public IP = " + publicIp); | |
| } | |
| } | |
| } else { | |
| // Manage the account-wide entry for the external firewall | |
| if (!createOrUpdateStatsEntry(create, accountId, zoneId, network.getId(), null, externalFirewall.getId(), firewallAnswer, false)) { | |
| throw new CloudRuntimeException(networkErrorMsg); | |
| } | |
| } | |
| } | |
| // If an external load balancer is added, manage one entry for each load balancing rule in this network | |
| if (externalLoadBalancer != null && lbAnswer != null) { | |
| boolean inline = _networkModel.isNetworkInlineMode(network); | |
| List<LoadBalancerVO> loadBalancers = _loadBalancerDao.listByNetworkIdAndScheme(network.getId(), Scheme.Public); | |
| for (LoadBalancerVO loadBalancer : loadBalancers) { | |
| String publicIp = _networkModel.getIp(loadBalancer.getSourceIpAddressId()).getAddress().addr(); | |
| if (!createOrUpdateStatsEntry(create, accountId, zoneId, network.getId(), publicIp, externalLoadBalancer.getId(), lbAnswer, inline)) { | |
| throw new CloudRuntimeException(networkErrorMsg + ", load balancing rule public IP = " + publicIp); | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| return true; | |
| } catch (Exception e) { | |
| logger.warn("Exception: ", e); | |
| return false; | |
| } | |
| } | |
| } | |
| } |