/**
* Abiquo community edition
* cloud management application for hybrid clouds
* Copyright (C) 2008-2010 - Abiquo Holdings S.L.
*
* This application is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC
* LICENSE as published by the Free Software Foundation under
* version 3 of the License
*
* This software 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
* LESSER GENERAL PUBLIC LICENSE v.3 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
package com.abiquo.scheduler.limit;
import java.util.HashMap;
import java.util.Map;
import javax.jms.ResourceAllocationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.abiquo.api.tracer.TracerLogger;
import com.abiquo.server.core.cloud.VirtualDatacenter;
import com.abiquo.server.core.common.DefaultEntityCurrentUsed;
import com.abiquo.server.core.common.DefaultEntityWithLimits;
import com.abiquo.server.core.common.DefaultEntityWithLimits.LimitStatus;
import com.abiquo.server.core.enterprise.Enterprise;
import com.abiquo.server.core.infrastructure.Datacenter;
import com.abiquo.server.core.scheduler.VirtualMachineRequirements;
import com.abiquo.server.core.scheduler.VirtualMachineRequirementsEmpty;
import com.abiquo.tracer.ComponentType;
import com.abiquo.tracer.EventType;
import com.abiquo.tracer.SeverityType;
/**
* Check the current range for the total resource allocation limits on a provided entity, indicating
* when exceed the soft or hard limit.
* <p>
*
* @param T {@link Enterprise}, {@link Datacenter} or {@link VirtualDataCenter}
*/
@Component
public abstract class EntityLimitChecker<T extends DefaultEntityWithLimits>
{
@Autowired
private TracerLogger tracer;
enum LimitResource
{
CPU, RAM, HD, STORAGE, VLAN, PUBLICIP;
}
/**
* Gets the limits defined on this entity.
*
* @param entity, the entity to obtain its resource allocation limits.
* @return null it haven't limits (so, don't check)<br>
* protected abstract ResourceAllocationLimitHB getLimit(CHECK_ENTITY entity);
*/
/**
* Get the sum of all the resources allocated by a given entity.
* <p>
*
* @param entity, the target entity of the allocation count.
* @return an ResourceAllocationLimit holding the total resource allocation on its <strong>hard
* limits</strong>, if some resource do not apply to the current entity set it to 0.
*/
protected abstract DefaultEntityCurrentUsed getCurrentUsed(T entity);
/**
* Create the entity identifier (used to trace)
*/
protected abstract String getEntityIdentifier(T entity);
/**
* Discard limits not applied on the current entity.
*
* @param limitStatus, the full list of resource limits.
* @return the ResourceLimitStatus that applies to the current entity.
*/
protected abstract Map<LimitResource, LimitStatus> getFilterResourcesStatus(
Map<LimitResource, LimitStatus> limitStatus);
/**
* Check the being use resource utilization for the given entity.
*
* @param entity, the entity to check its limit status.
* @throws ResourceAllocationException, if can not obtain the total resource allocation or
* LimitExceededException, only {@link HardLimitExceededException}, actually force
* the soft limit.
*/
public void checkCurrentLimits(final T entity)
{
checkLimits(entity, new VirtualMachineRequirementsEmpty(), true);
}
/**
* Check the resource allocation limits on the current entity when adding new resource
* requirements. Overloaded method because en case of deploying VM is not necessary check VLAN
* limits.
*
* @param entity, the entity to obtain its resource allocation limits.
* @param required, new target resource requirements.
* @param force, indicating if the soft limit reached should be reported as exception or only a
* <strong>event trace<strong> is created and the machine selection continues.
* @throws LimitExceededException, {@link HardLimitExceededException} it the resource allocation
* hard limit is exceeded. {@link SoftLimitExceededException}, on force = false and
* the soft limit is exceeded.
*/
public void checkLimits(final T entity, final VirtualMachineRequirements required,
final boolean force) throws LimitExceededException
{
checkLimits(entity, required, force, true, true);
}
public void checkLimits(final T entity, final VirtualMachineRequirements required,
final boolean force, final Boolean checkVLAN) throws LimitExceededException
{
checkLimits(entity, required, force, checkVLAN, false);
}
public void checkLimits(final T entity, final VirtualMachineRequirements required,
final boolean force, final Boolean checkVLAN, final Boolean checkIPs)
throws LimitExceededException
{
if (allNoLimits(entity))
{
return;
}
final DefaultEntityCurrentUsed actualAllocated = getCurrentUsed(entity);
Map<LimitResource, LimitStatus> entityResourceStatus =
getResourcesLimit(entity, actualAllocated, required, checkVLAN, checkIPs);
entityResourceStatus = getFilterResourcesStatus(entityResourceStatus);
checkResourceLimits(force, entityResourceStatus, entity, required, actualAllocated);
}
/**
* Gets the list of all resources with his limit status.
*/
private Map<LimitResource, LimitStatus> getResourcesLimit(final T limits,
final DefaultEntityCurrentUsed actualAllocated, final VirtualMachineRequirements required,
final Boolean checkVLAN, final Boolean checkIPs)
{
Map<LimitResource, LimitStatus> limitStatus =
new HashMap<EntityLimitChecker.LimitResource, DefaultEntityWithLimits.LimitStatus>();
// Initialized in order to show GUI's popup values
limitStatus.put(LimitResource.CPU, LimitStatus.OK);
limitStatus.put(LimitResource.RAM, LimitStatus.OK);
limitStatus.put(LimitResource.STORAGE, LimitStatus.OK);
limitStatus.put(LimitResource.HD, LimitStatus.OK);
limitStatus.put(LimitResource.VLAN, LimitStatus.OK);
// limitStatus.put(LimitResource.PUBLICIP, LimitStatus.OK);
if (required.getHd() >= 0)
{
long actualAndRequiredHd = actualAllocated.getHdInMb() + required.getHd();
limitStatus.put(LimitResource.HD, limits.checkHdStatus(actualAndRequiredHd));
}
int actualAndRequiredCpu = (int) (actualAllocated.getCpu() + required.getCpu());
int actualAndRequiredRam = (int) (actualAllocated.getRamInMb() + required.getRam());
long actualAndRequiredStorage = actualAllocated.getStorage() + required.getStorage();
if (checkVLAN)// && required.getPublicVLAN() != 0)
{
int actualAndRequiredVLANs =
(int) (actualAllocated.getVlanCount() + required.getPublicVLAN());
limitStatus.put(LimitResource.VLAN, limits.checkVlanStatus(actualAndRequiredVLANs));
}
if (checkIPs)// && required.getPublicIP() != 0
{
int actualAndRequiredIPs =
(int) (actualAllocated.getPublicIp() + required.getPublicIP());
limitStatus.put(LimitResource.PUBLICIP, limits
.checkPublicIpStatus(actualAndRequiredIPs));
}
limitStatus.put(LimitResource.CPU, limits.checkCpuStatus(actualAndRequiredCpu));
limitStatus.put(LimitResource.RAM, limits.checkRamStatus(actualAndRequiredRam));
limitStatus.put(LimitResource.STORAGE, limits.checkStorageStatus(actualAndRequiredStorage));
return limitStatus;
}
/**
* @return true if all the soft and hard limits are set to 0 (unlimited)
*/
private boolean allNoLimits(final T limit)
{
return limit.getCpuCountLimits().isNoLimit() //
&& limit.getRamLimitsInMb().isNoLimit() //
&& limit.getHdLimitsInMb().isNoLimit() //
&& (limit.getVlansLimits() == null || limit.getVlansLimits().isNoLimit()) //
&& (limit.getStorageLimits() == null || limit.getStorageLimits().isNoLimit()) //
&& (limit.getPublicIPLimits() == null || limit.getPublicIPLimits().isNoLimit());
}
/**
* Throws an exception if some of the considered resource limit status exceed some limit. Also
* trace when some are exceeded.
*/
private void checkResourceLimits(final boolean force,
final Map<LimitResource, LimitStatus> statusMap, final T entity,
final VirtualMachineRequirements requirements, final DefaultEntityCurrentUsed actual)
throws LimitExceededException
{
LimitStatus totalLimitStatus;
if (statusMap.containsValue(LimitStatus.HARD_LIMIT))
{
totalLimitStatus = LimitStatus.HARD_LIMIT;
}
else if (statusMap.containsValue(LimitStatus.SOFT_LIMIT))
{
totalLimitStatus = LimitStatus.SOFT_LIMIT;
}
else
{
totalLimitStatus = LimitStatus.OK;
}
if (totalLimitStatus != LimitStatus.OK)
{
LimitExceededException exc =
new LimitExceededException(totalLimitStatus == LimitStatus.HARD_LIMIT,
statusMap,
entity,
requirements,
actual,
getEntityIdentifier(entity));
// don't trace anything in tests.
traceLimit(totalLimitStatus == LimitStatus.HARD_LIMIT, force, entity, exc);
}
}
private void traceLimit(final boolean hard, final boolean force, final T entity,
final LimitExceededException except)
{
final String entityId = getEntityIdentifier(entity);
final EventType etype =
hard ? EventType.WORKLOAD_HARD_LIMIT_EXCEEDED : EventType.WORKLOAD_SOFT_LIMIT_EXCEEDED;
String traceMessage = String.format("Not enough resources on %s", entityId);
switch (traceSystem(entity))
{
case DETAIL:
traceMessage = except.toString();
case NO_DETAIL:
if (tracer != null)
{
tracer.systemLog(SeverityType.MAJOR, ComponentType.WORKLOAD, etype,
traceMessage);
}
break;
default:
break;
}
traceMessage = String.format("Not enough resources on %s", entityId);
switch (traceEnterprise(entity, force))
{
case DETAIL:
traceMessage = except.toString();
case NO_DETAIL:
if ((etype.equals(EventType.WORKLOAD_HARD_LIMIT_EXCEEDED) || entity instanceof VirtualDatacenter)
&& tracer != null)
{
tracer.log(SeverityType.MAJOR, ComponentType.WORKLOAD, etype, traceMessage);
}
break;
default:
break;
}
switch (returnExcption(entity, hard, force))
{
case DETAIL:
throw except;
case NO_DETAIL:
throw new LimitExceededExceptionNoDetail(except);
default:
break;
}
}
enum InformationSecurity
{
DETAIL, NO_DETAIL, IGNORE;
}
private InformationSecurity traceSystem(final T entity)
{
if (entity instanceof VirtualDatacenter)
{
return InformationSecurity.IGNORE;
}
return InformationSecurity.DETAIL;
}
private InformationSecurity traceEnterprise(final T entity, final boolean force)
{
if (entity instanceof VirtualDatacenter)
{
return force ? InformationSecurity.DETAIL : InformationSecurity.IGNORE;
}
return InformationSecurity.NO_DETAIL;
}
private InformationSecurity returnExcption(final T entity, final boolean hard,
final boolean force)
{
if (entity instanceof VirtualDatacenter)
{
if (hard)
{
return InformationSecurity.DETAIL;
}
else
{
return force ? InformationSecurity.IGNORE : InformationSecurity.DETAIL;
}
}
return hard ? InformationSecurity.NO_DETAIL : InformationSecurity.IGNORE;
}
}