/**
* 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.abiserver.scheduler.limit;
import com.abiquo.abiserver.business.hibernate.pojohb.infrastructure.DatacenterHB;
import com.abiquo.abiserver.business.hibernate.pojohb.user.EnterpriseHB;
import com.abiquo.abiserver.business.hibernate.pojohb.virtualappliance.VirtualDataCenterHB;
import com.abiquo.abiserver.business.hibernate.pojohb.virtualhardware.LimitHB;
import com.abiquo.abiserver.business.hibernate.pojohb.virtualhardware.ResourceAllocationLimitHB;
import com.abiquo.abiserver.commands.BasicCommand;
import com.abiquo.abiserver.persistence.DAOFactory;
import com.abiquo.abiserver.persistence.hibernate.HibernateDAOFactory;
import com.abiquo.abiserver.pojo.authentication.UserSession;
import com.abiquo.abiserver.pojo.infrastructure.DataCenter;
import com.abiquo.abiserver.scheduler.limit.exception.HardLimitExceededException;
import com.abiquo.abiserver.scheduler.limit.exception.LimitExceededException;
import com.abiquo.abiserver.scheduler.limit.exception.SoftLimitExceededException;
import com.abiquo.server.core.cloud.VirtualDatacenter;
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 CHECK_ENTITY {@link EnterpriseHB}, {@link DatacenterHB} or {@link VirtualDataCenterHB}
*/
public abstract class EntityLimitChecker<CHECK_ENTITY extends Object>
{
public enum LimitResource
{
STORAGE("storage"), VLAN("vlan"), PUBLICIP("public ip");
private String entityName;
private LimitResource(final String entityname)
{
this.entityName = entityname;
}
public String getEntityName()
{
return this.entityName;
}
}
/**
* Describe the total resource reservation requirements intervals.
*/
enum ResourceLimitRange
{
/** the current machine will not exceed any resource reservation requirements. */
OK,
/** the current machine will exceed the total recommended resource reservation. */
SOFT,
/** the current machine will exceed the total allowed resource reservation requirements. */
HARD
};
/**
* 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)
*/
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.
*/
abstract long getActualAllocation(CHECK_ENTITY entity, LimitResource resourceType);
abstract String getEntityName(CHECK_ENTITY entity);
/**
* Check the resource allocation limits on the current entity when adding new resource
* requirements.
*
* @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.
* @param editLimits, inidicates if we are editing limits to change tracer message.
* @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 CHECK_ENTITY entity, final LimitResource resourceType,
final long required, final boolean force, final UserSession userSession,
final VirtualDataCenterHB virtualDatacenter, final boolean editLimits)
{
ResourceAllocationLimitHB limits = getLimit(entity);
LimitHB limit = null;
switch (resourceType)
{
case STORAGE:
limit = limits.getStorage();
break;
case PUBLICIP:
limit = limits.getPublicIP();
break;
case VLAN:
limit = limits.getVlan();
break;
default:
break;
}
if (limit == null || noLimits(limit))
{
return;
}
final Long actual = getActualAllocation(entity, resourceType);
ResourceLimitRange status = limitStatus(actual, required, limit);
checkResourceLimits(force, status, resourceType, entity, required, actual, limit,
userSession, virtualDatacenter, editLimits);
}
/**
* Check the resource allocation limits on the current entity when adding new resource
* requirements.
*
* @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 CHECK_ENTITY entity, final LimitResource resourceType,
final long required, final boolean force, final UserSession userSession,
final VirtualDataCenterHB virtualDatacenter)
{
checkLimits(entity, resourceType, required, force, userSession, virtualDatacenter, false);
}
/**
* @return true if all the soft and hard limits are set to 0 (unlimited)
*/
private boolean noLimits(final LimitHB limit)
{
return limit == null || limit.getSoft() == 0 && limit.getHard() == 0;
}
/**
* Determine the {@link ResourceLimitRange} for the current actual resource utilization, the new
* resource requirements and the defined limits.
*
* @param actual, current being used resource count
* @param requried, new resource requirements
* @param limit, the entity limit
* @param type, the entity type
* @return a range indicating where the resource limit is.
*/
protected ResourceLimitRange limitStatus(final long actual, final long required,
final LimitHB limit)
{
final long current = actual + required;
ResourceLimitRange range;
if (current >= limit.getSoft())
{
if (current > limit.getHard() && limit.getHard() != 0)
{
range = ResourceLimitRange.HARD;
}
else if (limit.getSoft() != 0)
{
range = ResourceLimitRange.SOFT;
}
else
{
range = ResourceLimitRange.OK;
}
}
else
{
range = ResourceLimitRange.OK;
}
return range;
}
/**
* Throws an exception if some of the considered resource limit status exceed some limit.
*/
private void checkResourceLimits(final boolean force, final ResourceLimitRange status,
final LimitResource resourceType, final CHECK_ENTITY entity, final long requirements,
final long actual, final LimitHB limit, final UserSession userSession,
final VirtualDataCenterHB virtualDatacenter, final boolean editLimits)
throws SoftLimitExceededException, HardLimitExceededException
{
switch (status)
{
case HARD:
traceLimit(true, force, entity, new HardLimitExceededException(entity,
requirements,
actual,
limit,
resourceType,
editLimits), userSession, virtualDatacenter);
break;
case SOFT:
traceLimit(false, force, entity, new SoftLimitExceededException(entity,
requirements,
actual,
limit,
resourceType,
editLimits), userSession, virtualDatacenter);
break;
default: // OK
break;
}
}
private void traceLimit(final boolean hard, final boolean force, final CHECK_ENTITY entity,
final LimitExceededException except, final UserSession userSession,
final VirtualDataCenterHB virtualDatacenter)
{
final String entityId = getEntityName(entity);
final EventType etype =
hard ? EventType.WORKLOAD_HARD_LIMIT_EXCEEDED : EventType.WORKLOAD_SOFT_LIMIT_EXCEEDED;
DAOFactory daoF = HibernateDAOFactory.instance();
DatacenterHB dcHB = null;
DataCenter dc = null;
String vdcName = virtualDatacenter != null ? virtualDatacenter.getName() : null;
try
{
daoF.beginConnection();
dcHB = daoF.getDataCenterDAO().findById(virtualDatacenter.getIdDataCenter());
if (dcHB != null)
{
dc = dcHB.toPojo();
}
}
catch (Exception e)
{
}
finally
{
// daoF.endConnection();
}
String traceMessage = String.format("Not enough resources on %s", entityId);
switch (traceSystem(entity))
{
case DETAIL:
traceMessage = except.toString();
case NO_DETAIL:
BasicCommand.traceSystemLog(SeverityType.MAJOR, ComponentType.WORKLOAD, etype, dc,
vdcName, traceMessage, null, null, null);
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 VirtualDataCenterHB)
{
// TracerFactory.getTracer().log(SeverityType.MAJOR, ComponentType.WORKLOAD,
// etype, traceMessage);
BasicCommand.traceLog(SeverityType.MAJOR, ComponentType.WORKLOAD, etype,
userSession, dc, vdcName, traceMessage, null, null, null,
userSession.getUser(), userSession.getEnterpriseName());
}
break;
default:
break;
}
switch (returnExcption(entity, hard, force))
{
case DETAIL:
throw except;
case NO_DETAIL:
throw except; // TODO new LimitExceededExceptionNoDetail(except);
default:
break;
}
}
enum InformationSecurity
{
DETAIL, NO_DETAIL, IGNORE;
}
private InformationSecurity traceSystem(final CHECK_ENTITY entity)
{
if (entity instanceof VirtualDatacenter)
{
return InformationSecurity.IGNORE;
}
return InformationSecurity.DETAIL;
}
private InformationSecurity traceEnterprise(final CHECK_ENTITY entity, final boolean force)
{
if (entity instanceof VirtualDatacenter)
{
return force ? InformationSecurity.DETAIL : InformationSecurity.IGNORE;
}
return InformationSecurity.NO_DETAIL;
}
private InformationSecurity returnExcption(final CHECK_ENTITY 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;
}
}