From 1c5cddf0849ca3f8063a9341582e39e771ac91f9 Mon Sep 17 00:00:00 2001 From: Prachi Damle Date: Wed, 24 Apr 2013 15:40:39 -0700 Subject: [PATCH] Adding new DeploymentClusterPlanner interface --- .../deploy/DeploymentClusterPlanner.java | 45 ++++ .../com/cloud/deploy/DeploymentPlanner.java | 6 + .../deploy/DeploymentPlanningManagerImpl.java | 194 +++++++++++++++++- .../src/com/cloud/vm/UserVmManagerImpl.java | 4 - 4 files changed, 234 insertions(+), 15 deletions(-) create mode 100644 api/src/com/cloud/deploy/DeploymentClusterPlanner.java diff --git a/api/src/com/cloud/deploy/DeploymentClusterPlanner.java b/api/src/com/cloud/deploy/DeploymentClusterPlanner.java new file mode 100644 index 00000000000..4d8ee74071c --- /dev/null +++ b/api/src/com/cloud/deploy/DeploymentClusterPlanner.java @@ -0,0 +1,45 @@ +// 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.deploy; + +import java.util.List; +import com.cloud.exception.InsufficientServerCapacityException; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; + +/** + */ +public interface DeploymentClusterPlanner extends DeploymentPlanner { + /** + * This is called to determine list of possible clusters where a virtual + * machine can be deployed. + * + * @param vm + * virtual machine. + * @param plan + * deployment plan that tells you where it's being deployed to. + * @param avoid + * avoid these data centers, pods, clusters, or hosts. + * @return DeployDestination for that virtual machine. + */ + List orderClusters(VirtualMachineProfile vm, DeploymentPlan plan, + ExcludeList avoid) throws InsufficientServerCapacityException; + + + PlannerResourceUsage getResourceType(); + +} diff --git a/api/src/com/cloud/deploy/DeploymentPlanner.java b/api/src/com/cloud/deploy/DeploymentPlanner.java index 537dd314733..a17fabed572 100644 --- a/api/src/com/cloud/deploy/DeploymentPlanner.java +++ b/api/src/com/cloud/deploy/DeploymentPlanner.java @@ -35,6 +35,7 @@ import com.cloud.vm.VirtualMachineProfile; /** */ public interface DeploymentPlanner extends Adapter { + /** * plan is called to determine where a virtual machine should be running. * @@ -46,6 +47,7 @@ public interface DeploymentPlanner extends Adapter { * avoid these data centers, pods, clusters, or hosts. * @return DeployDestination for that virtual machine. */ + @Deprecated DeployDestination plan(VirtualMachineProfile vm, DeploymentPlan plan, ExcludeList avoid) throws InsufficientServerCapacityException; /** @@ -88,6 +90,10 @@ public interface DeploymentPlanner extends Adapter { userconcentratedpod_firstfit; } + public enum PlannerResourceUsage { + Shared, Dedicated; + } + public static class ExcludeList { private Set _dcIds; private Set _podIds; diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java index c7162a2003f..e03ffb17fad 100644 --- a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -17,9 +17,11 @@ package com.cloud.deploy; import java.util.List; +import java.util.Map; import javax.ejb.Local; import javax.inject.Inject; +import javax.naming.ConfigurationException; import org.apache.cloudstack.affinity.AffinityGroupProcessor; import org.apache.cloudstack.affinity.AffinityGroupVMMapVO; @@ -27,21 +29,44 @@ import org.apache.cloudstack.affinity.dao.AffinityGroupDao; import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; import org.apache.log4j.Logger; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; import com.cloud.deploy.DeploymentPlanner.ExcludeList; +import com.cloud.deploy.DeploymentPlanner.PlannerResourceUsage; +import com.cloud.deploy.dao.PlannerHostReservationDao; import com.cloud.exception.AffinityConflictException; +import com.cloud.exception.ConnectionException; import com.cloud.exception.InsufficientServerCapacityException; +import com.cloud.host.HostVO; +import com.cloud.host.Status; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.security.SecurityGroupVO; +import com.cloud.offering.ServiceOffering; +import com.cloud.utils.component.ComponentContext; import com.cloud.utils.component.Manager; import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Transaction; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; +import com.cloud.agent.AgentManager; +import com.cloud.agent.Listener; +import com.cloud.agent.api.AgentControlAnswer; +import com.cloud.agent.api.AgentControlCommand; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupRoutingCommand; @Local(value = { DeploymentPlanningManager.class }) -public class DeploymentPlanningManagerImpl extends ManagerBase implements DeploymentPlanningManager, Manager { +public class DeploymentPlanningManagerImpl extends ManagerBase implements DeploymentPlanningManager, Manager, Listener { private static final Logger s_logger = Logger.getLogger(DeploymentPlanningManagerImpl.class); @Inject + AgentManager _agentMgr; + @Inject protected UserVmDao _vmDao; @Inject protected VMInstanceDao _vmInstanceDao; @@ -49,6 +74,10 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy protected AffinityGroupDao _affinityGroupDao; @Inject protected AffinityGroupVMMapDao _affinityGroupVMMapDao; + @Inject + DataCenterDao _dcDao; + @Inject + PlannerHostReservationDao _plannerHostReserveDao; protected List _planners; public List getPlanners() { @@ -88,19 +117,162 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy // call planners DeployDestination dest = null; - for (DeploymentPlanner planner : _planners) { - if (planner.canHandle(vmProfile, plan, avoids)) { - dest = planner.plan(vmProfile, plan, avoids); - } else { - continue; - } - if (dest != null) { - avoids.addHost(dest.getHost().getId()); - break; - } + List clusterIds = null; + ServiceOffering offering = vmProfile.getServiceOffering(); + if (offering != null && offering.getDeploymentPlanner() != null) { + DeploymentPlanner planner = ComponentContext.getComponent(offering.getDeploymentPlanner()); + if (planner != null && planner.canHandle(vmProfile, plan, avoids)) { + while (true) { + if (planner instanceof DeploymentClusterPlanner) { + clusterIds = ((DeploymentClusterPlanner) planner).orderClusters(vmProfile, plan, avoids); + } else { + dest = planner.plan(vmProfile, plan, avoids); + } + + if (dest != null) { + long hostId = dest.getHost().getId(); + avoids.addHost(dest.getHost().getId()); + + if (checkIfHostCanBeUsed(hostId, DeploymentPlanner.PlannerResourceUsage.Shared)) { + // found destination + return dest; + } else { + // find another host - seems some concurrent deployment picked it up for dedicated access + continue; + } + } else if (clusterIds != null && !clusterIds.isEmpty()) { + // planner refactoring. call allocators to list hosts + + } else { + return null; + } + } + } } + + return dest; } + @DB + private boolean checkIfHostCanBeUsed(long hostId, PlannerResourceUsage resourceTypeRequired) { + // TODO Auto-generated method stub + // check if this host has been picked up by some other planner + // exclusively + // if planner can work with shared host, check if this host has + // been marked as 'shared' + // else if planner needs dedicated host, + + PlannerHostReservationVO reservationEntry = _plannerHostReserveDao.findByHostId(hostId); + if (reservationEntry != null) { + long id = reservationEntry.getId(); + PlannerResourceUsage hostResourceType = reservationEntry.getResourceUsage(); + + if (hostResourceType != null) { + if (hostResourceType == resourceTypeRequired) { + return true; + } else { + return false; + } + } else { + // reserve the host for required resourceType + // let us lock the reservation entry before updating. + final Transaction txn = Transaction.currentTxn(); + + try { + txn.start(); + + final PlannerHostReservationVO lockedEntry = _plannerHostReserveDao.lockRow(id, true); + if (lockedEntry == null) { + s_logger.error("Unable to lock the host entry for reservation, host: " + hostId); + return false; + } + // check before updating + if (lockedEntry.getResourceUsage() == null) { + lockedEntry.setResourceUsage(resourceTypeRequired); + _plannerHostReserveDao.persist(lockedEntry); + return true; + } else { + // someone updated it earlier. check if we can still use it + if (lockedEntry.getResourceUsage() == resourceTypeRequired) { + return true; + } else { + return false; + } + } + } finally { + txn.commit(); + } + } + + } + + return false; + } + + @Override + public boolean processAnswers(long agentId, long seq, Answer[] answers) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean processCommands(long agentId, long seq, Command[] commands) { + // TODO Auto-generated method stub + return false; + } + + @Override + public AgentControlAnswer processControlCommand(long agentId, AgentControlCommand cmd) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void processConnect(HostVO host, StartupCommand cmd, boolean forRebalance) throws ConnectionException { + if (!(cmd instanceof StartupRoutingCommand)) { + return; + } + + PlannerHostReservationVO reservationEntry = _plannerHostReserveDao.findByHostId(host.getId()); + if (reservationEntry == null) { + // record the host in this table + PlannerHostReservationVO newHost = new PlannerHostReservationVO(host.getId(), host.getDataCenterId(), + host.getPodId(), host.getClusterId()); + _plannerHostReserveDao.persist(newHost); + } + + } + + @Override + public boolean processDisconnect(long agentId, Status state) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isRecurring() { + // TODO Auto-generated method stub + return false; + } + + @Override + public int getTimeout() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean processTimeout(long agentId, long seq) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean configure(final String name, final Map params) throws ConfigurationException { + _agentMgr.registerForHostEvents(this, true, false, true); + return super.configure(name, params); + } + } diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index af2eb776c0a..5e81435d2d8 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -97,7 +97,6 @@ import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.deploy.DataCenterDeployment; import com.cloud.deploy.DeployDestination; -import com.cloud.deploy.DeployPlannerSelector; import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; @@ -401,9 +400,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use @Inject AffinityGroupDao _affinityGroupDao; - @Inject - List plannerSelectors; - protected ScheduledExecutorService _executor = null; protected int _expungeInterval; protected int _expungeDelay;