/*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is available at http://www.abiquo.com/.....
*
* The Initial Developer of the Original Code is Soluciones Grid, S.L. (www.abiquo.com),
* Consell de Cent 296, Principal 2�, 08007 Barcelona, Spain.
*
* No portions of the Code have been created by third parties.
* All Rights Reserved.
*
* Contributor(s): ______________________________________.
*
* Graphical User Interface of this software may be used under the terms
* of the Common Public Attribution License Version 1.0 (the "CPAL License",
* available at http://cpal.abiquo.com/), in which case the
* provisions of CPAL License are applicable instead of those above. In relation
* of this portions of the Code, a Legal Notice according to Exhibits A and B of
* CPAL Licence should be provided in any distribution of the corresponding Code
* to Graphical User Interface.
*/
package com.abiquo.abiserver.scheduler;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import com.abiquo.abiserver.business.hibernate.pojohb.infrastructure.HypervisorHB;
import com.abiquo.abiserver.business.hibernate.pojohb.infrastructure.PhysicalmachineHB;
import com.abiquo.abiserver.business.hibernate.util.HibernateUtil;
import com.abiquo.abiserver.pojo.infrastructure.HyperVisor;
import com.abiquo.abiserver.pojo.infrastructure.PhysicalMachine;
import com.abiquo.abiserver.pojo.infrastructure.State;
import com.abiquo.abiserver.pojo.infrastructure.VirtualMachine;
import com.abiquo.abiserver.pojo.virtualimage.VirtualImage;
import org.apache.log4j.Logger;
import org.hibernate.Query;
import org.hibernate.Session;
/**
* Abstract IScheduler implementation, contains database transactions (Hibernate ->DB layer).
* Selects form database only the PhysicalMachines with enough cpu, ram and hd on the selectMachine
* function. Convert ''selectMachines'' to ''select'' with a PhysicalMachines candidate (all capable
* to deploy the image) list.
*/
public abstract class SchedulerRestrictions implements IScheduler
{
/** The logger object */
private final static Logger log = Logger.getLogger(SchedulerRestrictions.class);
/** The remote desktop min port **/
private final static int minRemoteDesktopPort = 3389;
/**
* Select one PhysicalMachine form the candidate list. Strategy pattern implementation.
*
* @param candidateMachines a list of PhysicalMachines with enough resources for the target
* VirtualImage.
* @return a PhysicalMachine contained on candidateMachines.
*/
public abstract PhysicalMachine select(List<PhysicalMachine> candidateMachines);
/**
* @see com.abiquo.abiserver.scheduler.IScheduler#selectMachine
*/
public VirtualMachine selectMachine(VirtualImage targetImage, String dataCenter)
throws SchedulerException
{
ImageRequired required;
List<PhysicalMachine> physicalCandidates;
PhysicalMachine physicalTarget;
VirtualMachine virtualTarget;
required =
new ImageRequired(targetImage.getCpuRequired(),
targetImage.getHdRequired(),
targetImage.getRamRequired(),
dataCenter);
log.debug("TargetImage name:" + targetImage.getName() + "at dataCenter:" + dataCenter
+ " REQUIRE hd:" + required.getHd() + " ram:" + required.getRam());
physicalCandidates = getFilteredMachines(required);
log.debug("There are " + physicalCandidates.size() + "physical machine candidates ");
physicalTarget = select(physicalCandidates);
log.info("Selected PhysicalMachine !!! " + physicalTarget.getName());
updateUsageDeployedPhysicalMachine(physicalTarget, required, true);
log.debug("Updated physical resources usage");
virtualTarget = instantiate(physicalTarget, targetImage);
log.debug("Instantiated VirtualMachine " + virtualTarget.getName());
return virtualTarget;
}
/**
* @see com.abiquo.abiserver.scheduler.IScheduler#selectMachine Iterative: requirements are take
* one by one. (TODO: requirement sort)
*/
public List<VirtualMachine> selectMachines(List<VirtualImage> targetImages, String dataCenter)
throws SchedulerException
{
List<VirtualMachine> virtuals;
virtuals = new ArrayList<VirtualMachine>();
log.warn("Iterative allocate multiple VirtualMachines");
for (VirtualImage image : targetImages)
{
virtuals.add(selectMachine(image, dataCenter));
}
return virtuals;
}
/**
* Get all the available PhysicalMachines with enough resources for the VirtualImage. It access
* DB throw Hibernate.
*
* @param required, the resources capacity used on the target VirtualImage (ram, hd,
* dataCenter).
* @return all PhysicalMachines available to instantiate a VirtualMachine for the target
* VirtualImage.
* @throws SchedulerException, if there is not any PhysicalMachine with enough resources.
*/
@SuppressWarnings("unchecked")
// PhysicalmachineHB
private List<PhysicalMachine> getFilteredMachines(ImageRequired required)
throws SchedulerException
{
List<PhysicalMachine> machines;
List<PhysicalmachineHB> machinesHibernate;
Query physicalFiler;
Session session;
session = HibernateUtil.getSession();
physicalFiler =
session
.createQuery("select pm "
+ "from com.abiquo.abiserver.business.hibernate.pojohb.infrastructure.PhysicalmachineHB pm "
+ "join pm.rack rk " + "join rk.datacenter dc " + "where " + "dc.name in ('"
+ required.getDataCenter() + "') and " + "(pm.ram - pm.ramUsed) >= "
+ required.getRam() + " " + "and (pm.hd - pm.hdUsed) >= " + required.getHd());
log.debug("HQL : " + physicalFiler.getQueryString());
machinesHibernate = physicalFiler.list();
if (machinesHibernate.size() == 0)
{
throw new SchedulerException("Not enough physical machine capacity to instantiate the required virtual appliance",
required);
}
machines = new ArrayList<PhysicalMachine>();
for (PhysicalmachineHB machineHib : machinesHibernate)
{
log.debug("PhysicalMachine candidate : " + machineHib.getName());
machines.add((PhysicalMachine) machineHib.toPojo());
}
return machines;
}
/**
* Set the resource usage on PhysicalMachine after instantiating the new VirtualMachine. It
* access DB throw Hibernate.
*
* @param machine, the machine to reduce/increase its resource capacity.
* @param used, the VirtualImage requirements to substract/add.
* @param isAdd, true if reducing the amount of resources on the PhysicalMachine. Else it adds
* capacity (as a rollback on VirtualImage deploy Exception).
*/
private void updateUsageDeployedPhysicalMachine(PhysicalMachine machine, ImageRequired used,
boolean isReducing)
{
PhysicalmachineHB machineHib;
Session session;
session = HibernateUtil.getSession();
log.debug("ram used: " + used.getRam() + "hd used: " + used.getHd());
machineHib = (PhysicalmachineHB) session.get(PhysicalmachineHB.class, machine.getId());
if (isReducing)
{
// machineHib.setCpuUsed(machineHib.getCpuUsed().add(used.getCpu()));
machineHib.setRamUsed(machineHib.getRamUsed() + used.getRam());
machineHib.setHdUsed(machineHib.getHdUsed() + used.getHd());
}
else
{
// machineHib.setCpuUsed(machineHib.getCpuUsed().subtract(used.getCpu()));
machineHib.setRamUsed(machineHib.getRamUsed() - used.getRam());
machineHib.setHdUsed(machineHib.getHdUsed() - used.getHd());
}
session.update(machineHib);
}
/**
* Create a Virtual Machine on the given PhysicalMachine to deploy the given VirtualMachine.
*
* @param physical, the target PhysicalMachine where create a new VirtualMachine (using
* PhysicalMachine's Hypervisor)
* @param image, the target VirtaulImage to be deployed on the returned VirtualMachine (used to
* allocate resource usage)
* @return a new VirtualMachine instance inside physical to load image. TODO: creating default
* Hypervisor instance TODO: VdrpIP, VdrpPort
*/
private VirtualMachine instantiate(PhysicalMachine physical, VirtualImage image)
{
HyperVisor hypervisor;
VirtualMachine virtual;
// The hypervisors shall be discovered when the physical machine are loaded so we recover
// the hypervisors from the DB
Session session;
PhysicalmachineHB physicalMachineHB;
Set<HypervisorHB> hypervisorsHB;
session = HibernateUtil.getSession();
physicalMachineHB =
(PhysicalmachineHB) session.get(PhysicalmachineHB.class, physical.getId());
hypervisorsHB = physicalMachineHB.getHypervisors();
// Getting one hypervisor from the list. TODO Change this decision when multihypervisors are
// used
HypervisorHB hypervisorHB = hypervisorsHB.iterator().next();
log.info("The hypervisor: " + hypervisorHB.getType().getName() + " will be used");
hypervisor = (HyperVisor) hypervisorHB.toPojo();
State state = new State();
// state.setDescription(statePojo.getDescription());
state.setId(State.NOT_DEPLOYED);
// TODO default also high disponibility flag)
virtual = new VirtualMachine();
virtual.setVirtualImage(image);
virtual.setName(UUID.randomUUID().toString()); // TODO
virtual.setDescription(image.getDescription());
virtual.setCpu(image.getCpuRequired());
virtual.setHd(image.getHdRequired());
virtual.setRam(image.getRamRequired());
virtual.setState(state);
// Setting the default VRDP acces
virtual.setVdrpIP(hypervisor.getIp());
// Selecting the max port
Integer rdpPort = null;
Integer maxRdpPort =
(Integer) session
.createQuery(
"select MAX(vdrpPort)"
+ "from com.abiquo.abiserver.business.hibernate.pojohb.infrastructure.VirtualmachineHB "
+ "where idHypervisor = " + hypervisor.getId()).uniqueResult();
if (maxRdpPort == null)
{
rdpPort = minRemoteDesktopPort;
}
else
{
rdpPort = maxRdpPort + 1;
log.debug("The assigned port is: " + rdpPort);
}
virtual.setVdrpPort(rdpPort);
virtual.setUUID(UUID.randomUUID().toString());
virtual.setAssignedTo(hypervisor);
/**
* TODO virtualMachine.setHighDisponibility(virtualMachinePojo.get)
* virtual.setVdrpIP(virtualMachinePojo.getVdrpIp());
* virtual.setVdrpPort(virtualMachinePojo.getVdrpPort());
**/
return virtual;
}
/**
* @param physicalMachine
* @see com.abiquo.abiserver.scheduler.IScheduler#rollback
*/
public void rollback(VirtualMachine virtual, PhysicalMachine physicalMachine)
{
ImageRequired required;
required =
new ImageRequired(virtual.getCpu(), virtual.getHd(), virtual.getRam(), "nodatacenter");
updateUsageDeployedPhysicalMachine(physicalMachine, required, false);
}
}
/**
* Pojo class maintaining the resource requirements for a VirtualImage
*/
class ImageRequired
{
int cpu;
long hd;
int ram;
String dataCenter;
public ImageRequired(int i, long l, int ram, String dataCenter)
{
this.cpu = i;
this.hd = l;
this.ram = ram;
this.dataCenter = dataCenter;
}
public String getDataCenter()
{
return dataCenter;
}
public void setDataCenter(String dataCenter)
{
this.dataCenter = dataCenter;
}
public int getCpu()
{
return cpu;
}
public long getHd()
{
return hd;
}
public int getRam()
{
return ram;
}
}