diff --git a/api/src/com/cloud/deploy/DeploymentClusterPlanner.java b/api/src/com/cloud/deploy/DeploymentClusterPlanner.java new file mode 100644 index 00000000000..1a19c71dbfa --- /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 getResourceUsage(); + +} diff --git a/api/src/com/cloud/deploy/DeploymentPlanner.java b/api/src/com/cloud/deploy/DeploymentPlanner.java index 537dd314733..eb56a591f6b 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; @@ -99,10 +105,22 @@ public interface DeploymentPlanner extends Adapter { } public ExcludeList(Set _dcIds, Set _podIds, Set _clusterIds, Set _hostIds, Set _poolIds) { - this._dcIds = _dcIds; - this._podIds = _podIds; - this._clusterIds = _clusterIds; - this._poolIds = _poolIds; + if (_dcIds != null) { + this._dcIds = new HashSet(_dcIds); + } + if (_podIds != null) { + this._podIds = new HashSet(_podIds); + } + if (_clusterIds != null) { + this._clusterIds = new HashSet(_clusterIds); + } + + if (_hostIds != null) { + this._hostIds = new HashSet(_hostIds); + } + if (_poolIds != null) { + this._poolIds = new HashSet(_poolIds); + } } public boolean add(InsufficientCapacityException e) { diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index e317a8fbf1c..ee7f5b7d89f 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -423,6 +423,7 @@ public class EventTypes { public static final String EVENT_INTERNAL_LB_VM_START = "INTERNALLBVM.START"; public static final String EVENT_INTERNAL_LB_VM_STOP = "INTERNALLBVM.STOP"; + public static final String EVENT_HOST_RESERVATION_RELEASE = "HOST.RESERVATION.RELEASE"; // Dedicated guest vlan range public static final String EVENT_GUEST_VLAN_RANGE_DEDICATE = "GUESTVLANRANGE.DEDICATE"; public static final String EVENT_DEDICATED_GUEST_VLAN_RANGE_RELEASE = "GUESTVLANRANGE.RELEASE"; @@ -728,7 +729,6 @@ public class EventTypes { entityEventDetails.put(EVENT_AUTOSCALEVMGROUP_UPDATE, AutoScaleVmGroup.class.getName()); entityEventDetails.put(EVENT_AUTOSCALEVMGROUP_ENABLE, AutoScaleVmGroup.class.getName()); entityEventDetails.put(EVENT_AUTOSCALEVMGROUP_DISABLE, AutoScaleVmGroup.class.getName()); - entityEventDetails.put(EVENT_GUEST_VLAN_RANGE_DEDICATE, GuestVlan.class.getName()); entityEventDetails.put(EVENT_DEDICATED_GUEST_VLAN_RANGE_RELEASE, GuestVlan.class.getName()); } diff --git a/api/src/com/cloud/offering/ServiceOffering.java b/api/src/com/cloud/offering/ServiceOffering.java index 165369c5e9b..45d5f38952b 100755 --- a/api/src/com/cloud/offering/ServiceOffering.java +++ b/api/src/com/cloud/offering/ServiceOffering.java @@ -108,4 +108,6 @@ public interface ServiceOffering extends InfrastructureEntity, InternalIdentity, boolean getDefaultUse(); String getSystemVmType(); + + String getDeploymentPlanner(); } diff --git a/api/src/com/cloud/resource/ResourceService.java b/api/src/com/cloud/resource/ResourceService.java index 08e2585d1a7..ce0df635bfe 100755 --- a/api/src/com/cloud/resource/ResourceService.java +++ b/api/src/com/cloud/resource/ResourceService.java @@ -100,11 +100,13 @@ public interface ResourceService { Swift discoverSwift(AddSwiftCmd addSwiftCmd) throws DiscoveryException; S3 discoverS3(AddS3Cmd cmd) throws DiscoveryException; - + List getSupportedHypervisorTypes(long zoneId, boolean forVirtualRouter, Long podId); Pair, Integer> listSwifts(ListSwiftsCmd cmd); List listS3s(ListS3sCmd cmd); + boolean releaseHostReservation(Long hostId); + } diff --git a/api/src/com/cloud/server/ManagementService.java b/api/src/com/cloud/server/ManagementService.java index 22494072648..59b83c9bbce 100755 --- a/api/src/com/cloud/server/ManagementService.java +++ b/api/src/com/cloud/server/ManagementService.java @@ -419,5 +419,7 @@ public interface ManagementService { * @return List of capacities */ List listTopConsumedResources(ListCapacityCmd cmd); + + List listDeploymentPlanners(); } diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index f09a00b98f8..8d7739c13e1 100755 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -496,6 +496,7 @@ public class ApiConstants { public static final String AFFINITY_GROUP_NAMES = "affinitygroupnames"; public static final String ASA_INSIDE_PORT_PROFILE = "insideportprofile"; public static final String AFFINITY_GROUP_ID = "affinitygroupid"; + public static final String DEPLOYMENT_PLANNER = "deploymentplanner"; public static final String ACL_ID = "aclid"; public static final String NUMBER = "number"; diff --git a/api/src/org/apache/cloudstack/api/command/admin/config/ListDeploymentPlannersCmd.java b/api/src/org/apache/cloudstack/api/command/admin/config/ListDeploymentPlannersCmd.java new file mode 100644 index 00000000000..598b620c301 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/config/ListDeploymentPlannersCmd.java @@ -0,0 +1,71 @@ +// 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 org.apache.cloudstack.api.command.admin.config; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.response.DeploymentPlannersResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.log4j.Logger; + +@APICommand(name = "listDeploymentPlanners", description = "Lists all DeploymentPlanners available.", responseObject = DeploymentPlannersResponse.class) +public class ListDeploymentPlannersCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(ListDeploymentPlannersCmd.class.getName()); + + private static final String s_name = "listdeploymentplannersresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute(){ + List planners = _mgr.listDeploymentPlanners(); + ListResponse response = new ListResponse(); + List plannerResponses = new ArrayList(); + + for (String planner : planners) { + DeploymentPlannersResponse plannerResponse = new DeploymentPlannersResponse(); + plannerResponse.setName(planner); + plannerResponse.setObjectName("deploymentPlanner"); + plannerResponses.add(plannerResponse); + } + + response.setResponses(plannerResponses); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + + } +} diff --git a/api/src/org/apache/cloudstack/api/command/admin/host/ReleaseHostReservationCmd.java b/api/src/org/apache/cloudstack/api/command/admin/host/ReleaseHostReservationCmd.java new file mode 100644 index 00000000000..d09cf38cc50 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/host/ReleaseHostReservationCmd.java @@ -0,0 +1,105 @@ +// 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 org.apache.cloudstack.api.command.admin.host; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.log4j.Logger; + +import com.cloud.async.AsyncJob; +import com.cloud.event.EventTypes; +import com.cloud.user.Account; +import com.cloud.user.UserContext; + +@APICommand(name = "releaseHostReservation", description = "Releases host reservation.", responseObject = SuccessResponse.class) +public class ReleaseHostReservationCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(ReleaseHostReservationCmd.class.getName()); + + private static final String s_name = "releasehostreservationresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType=HostResponse.class, + required=true, description="the host ID") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + Account account = UserContext.current().getCaller(); + if (account != null) { + return account.getId(); + } + + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_HOST_RESERVATION_RELEASE; + } + + @Override + public String getEventDescription() { + return "releasing reservation for host: " + getId(); + } + + @Override + public AsyncJob.Type getInstanceType() { + return AsyncJob.Type.Host; + } + + @Override + public Long getInstanceId() { + return getId(); + } + + @Override + public void execute(){ + boolean result = _resourceService.releaseHostReservation(getId()); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to release host reservation"); + } + } +} diff --git a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java index 0e35276d914..c155b706fc0 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java @@ -84,6 +84,9 @@ public class CreateServiceOfferingCmd extends BaseCmd { @Parameter(name=ApiConstants.NETWORKRATE, type=CommandType.INTEGER, description="data transfer rate in megabits per second allowed. Supported only for non-System offering and system offerings having \"domainrouter\" systemvmtype") private Integer networkRate; + @Parameter(name = ApiConstants.DEPLOYMENT_PLANNER, type = CommandType.STRING, description = "The deployment planner heuristics used to deploy a VM of this offering. If null, value of global config vm.deployment.planner is used") + private String deploymentPlanner; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -148,6 +151,9 @@ public class CreateServiceOfferingCmd extends BaseCmd { return networkRate; } + public String getDeploymentPlanner() { + return deploymentPlanner; + } ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// diff --git a/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java b/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java index f35e87e3b0f..08ebbb05887 100644 --- a/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java +++ b/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java @@ -18,6 +18,8 @@ package org.apache.cloudstack.api.response; import java.util.Date; +import javax.persistence.Column; + import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; @@ -82,6 +84,8 @@ public class ServiceOfferingResponse extends BaseResponse { @SerializedName(ApiConstants.NETWORKRATE) @Param(description="data transfer rate in megabits per second allowed.") private Integer networkRate; + @SerializedName(ApiConstants.DEPLOYMENT_PLANNER) @Param(description="deployment strategy used to deploy VM.") + private String deploymentPlanner; public String getId() { return id; @@ -225,4 +229,12 @@ public class ServiceOfferingResponse extends BaseResponse { public void setNetworkRate(Integer networkRate) { this.networkRate = networkRate; } + + public String getDeploymentPlanner() { + return deploymentPlanner; + } + + public void setDeploymentPlanner(String deploymentPlanner) { + this.deploymentPlanner = deploymentPlanner; + } } diff --git a/client/tomcatconf/applicationContext.xml.in b/client/tomcatconf/applicationContext.xml.in index 6406660c814..1d1eca4c191 100644 --- a/client/tomcatconf/applicationContext.xml.in +++ b/client/tomcatconf/applicationContext.xml.in @@ -540,15 +540,11 @@ Deployment planners --> - - - + - - - + @@ -605,10 +601,6 @@ - - - - @@ -623,6 +615,7 @@ + @@ -630,9 +623,7 @@ - - - + @@ -833,17 +824,13 @@ - + - - - - @@ -859,6 +846,8 @@ + + @@ -868,4 +857,7 @@ + + + diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index f57cc3c1815..4cd9065b641 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -212,6 +212,7 @@ listConfigurations=1 ldapConfig=1 ldapRemove=1 listCapabilities=15 +listDeploymentPlanners=1 #### pod commands createPod=1 @@ -261,6 +262,7 @@ listHosts=3 findHostsForMigration=1 addSecondaryStorage=1 updateHostPassword=1 +releaseHostReservation=1 #### volume commands attachVolume=15 diff --git a/engine/schema/src/com/cloud/migration/ServiceOffering21VO.java b/engine/schema/src/com/cloud/migration/ServiceOffering21VO.java index d07be6462f1..7a49e63e5b3 100644 --- a/engine/schema/src/com/cloud/migration/ServiceOffering21VO.java +++ b/engine/schema/src/com/cloud/migration/ServiceOffering21VO.java @@ -174,5 +174,10 @@ public class ServiceOffering21VO extends DiskOffering21VO implements ServiceOffe return false; } + @Override + public String getDeploymentPlanner() { + // TODO Auto-generated method stub + return null; + } } diff --git a/engine/schema/src/com/cloud/service/ServiceOfferingVO.java b/engine/schema/src/com/cloud/service/ServiceOfferingVO.java index 94a73515e6a..fd31d301bc3 100755 --- a/engine/schema/src/com/cloud/service/ServiceOfferingVO.java +++ b/engine/schema/src/com/cloud/service/ServiceOfferingVO.java @@ -68,6 +68,9 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering @Column(name="sort_key") int sortKey; + @Column(name = "deployment_planner") + private String deploymentPlanner = null; + protected ServiceOfferingVO() { super(); } @@ -104,6 +107,15 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering this.hostTag = hostTag; } + public ServiceOfferingVO(String name, int cpu, int ramSize, int speed, Integer rateMbps, Integer multicastRateMbps, + boolean offerHA, boolean limitResourceUse, boolean volatileVm, String displayText, boolean useLocalStorage, + boolean recreatable, String tags, boolean systemUse, VirtualMachine.Type vm_type, Long domainId, + String hostTag, String deploymentPlanner) { + this(name, cpu, ramSize, speed, rateMbps, multicastRateMbps, offerHA, limitResourceUse, volatileVm, + displayText, useLocalStorage, recreatable, tags, systemUse, vm_type, domainId, hostTag); + this.deploymentPlanner = deploymentPlanner; + } + @Override public boolean getOfferHA() { return offerHA; @@ -208,4 +220,9 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering return volatileVm; } + @Override + public String getDeploymentPlanner() { + return deploymentPlanner; + } + } diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java index 1d35c8981fa..c03d377cbe0 100644 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java @@ -17,6 +17,10 @@ package com.cloud.upgrade.dao; +import com.cloud.deploy.DeploymentPlanner; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; +import org.apache.log4j.Logger; import java.io.File; import java.sql.Connection; import java.sql.Date; @@ -25,12 +29,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.UUID; - import com.cloud.network.vpc.NetworkACL; -import org.apache.log4j.Logger; - -import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.script.Script; public class Upgrade410to420 implements DbUpgrade { final static Logger s_logger = Logger.getLogger(Upgrade410to420.class); @@ -70,6 +69,7 @@ public class Upgrade410to420 implements DbUpgrade { updatePrimaryStore(conn); addEgressFwRulesForSRXGuestNw(conn); upgradeEIPNetworkOfferings(conn); + updateGlobalDeploymentPlanner(conn); upgradeDefaultVpcOffering(conn); upgradePhysicalNtwksWithInternalLbProvider(conn); updateNetworkACLs(conn); @@ -563,6 +563,53 @@ public class Upgrade410to420 implements DbUpgrade { } } + private void updateGlobalDeploymentPlanner(Connection conn) { + PreparedStatement pstmt = null; + ResultSet rs = null; + + try { + pstmt = conn + .prepareStatement("select value from `cloud`.`configuration` where name = 'vm.allocation.algorithm'"); + rs = pstmt.executeQuery(); + while (rs.next()) { + String globalValue = rs.getString(1); + String plannerName = "FirstFitPlanner"; + + if (globalValue != null) { + if (globalValue.equals(DeploymentPlanner.AllocationAlgorithm.random.toString())) { + plannerName = "FirstFitPlanner"; + } else if (globalValue.equals(DeploymentPlanner.AllocationAlgorithm.firstfit.toString())) { + plannerName = "FirstFitPlanner"; + } else if (globalValue.equals(DeploymentPlanner.AllocationAlgorithm.userconcentratedpod_firstfit + .toString())) { + plannerName = "UserConcentratedPodPlanner"; + } else if (globalValue.equals(DeploymentPlanner.AllocationAlgorithm.userconcentratedpod_random + .toString())) { + plannerName = "UserConcentratedPodPlanner"; + } else if (globalValue.equals(DeploymentPlanner.AllocationAlgorithm.userdispersing.toString())) { + plannerName = "UserDispersingPlanner"; + } + } + // update vm.deployment.planner global config + pstmt = conn.prepareStatement("UPDATE `cloud`.`configuration` set value=? where name = 'vm.deployment.planner'"); + pstmt.setString(1, plannerName); + pstmt.executeUpdate(); + } + } catch (SQLException e) { + throw new CloudRuntimeException("Unable to set vm.deployment.planner global config", e); + } finally { + try { + if (rs != null) { + rs.close(); + } + if (pstmt != null) { + pstmt.close(); + } + } catch (SQLException e) { + } + } + } + private void upgradeDefaultVpcOffering(Connection conn) { PreparedStatement pstmt = null; @@ -596,8 +643,6 @@ public class Upgrade410to420 implements DbUpgrade { } } - - private void upgradePhysicalNtwksWithInternalLbProvider(Connection conn) { PreparedStatement pstmt = null; @@ -644,7 +689,6 @@ public class Upgrade410to420 implements DbUpgrade { } catch (SQLException e) { } } - } private void addHostDetailsIndex(Connection conn) { diff --git a/engine/schema/src/com/cloud/vm/dao/VMInstanceDao.java b/engine/schema/src/com/cloud/vm/dao/VMInstanceDao.java index c604027abde..830e4643251 100644 --- a/engine/schema/src/com/cloud/vm/dao/VMInstanceDao.java +++ b/engine/schema/src/com/cloud/vm/dao/VMInstanceDao.java @@ -38,14 +38,14 @@ public interface VMInstanceDao extends GenericDao, StateDao< * @return list of VMInstanceVO running on that host. */ List listByHostId(long hostId); - + /** * List VMs by zone ID * @param zoneId * @return list of VMInstanceVO in the specified zone */ List listByZoneId(long zoneId); - + /** * List VMs by pod ID * @param podId @@ -59,32 +59,32 @@ public interface VMInstanceDao extends GenericDao, StateDao< * @return list of VMInstanceVO in the specified zone, deployed from the specified template, that are not expunged */ public List listNonExpungedByZoneAndTemplate(long zoneId, long templateId); - + /** * Find vm instance with names like. - * + * * @param name name that fits SQL like. * @return list of VMInstanceVO */ List findVMInstancesLike(String name); - + List findVMInTransition(Date time, State... states); List listByTypes(VirtualMachine.Type... types); - + VMInstanceVO findByIdTypes(long id, VirtualMachine.Type... types); - + VMInstanceVO findVMByInstanceName(String name); void updateProxyId(long id, Long proxyId, Date time); List listByHostIdTypes(long hostid, VirtualMachine.Type... types); - + List listUpByHostIdTypes(long hostid, VirtualMachine.Type... types); List listByZoneIdAndType(long zoneId, VirtualMachine.Type type); List listUpByHostId(Long hostId); List listByLastHostId(Long hostId); - + List listByTypeAndState(VirtualMachine.Type type, State state); List listByAccountId(long accountId); @@ -92,9 +92,9 @@ public interface VMInstanceDao extends GenericDao, StateDao< List listByClusterId(long clusterId); // this does not pull up VMs which are starting List listLHByClusterId(long clusterId); // get all the VMs even starting one on this cluster - + List listVmsMigratingFromHost(Long hostId); - + public Long countRunningByHostId(long hostId); Pair, Map> listClusterIdsInZoneByVmCount(long zoneId, long accountId); @@ -106,7 +106,7 @@ public interface VMInstanceDao extends GenericDao, StateDao< List listHostIdsByVmCount(long dcId, Long podId, Long clusterId, long accountId); Long countRunningByAccount(long accountId); - + List listNonRemovedVmsByTypeAndNetwork(long networkId, VirtualMachine.Type... types); /** @@ -116,4 +116,8 @@ public interface VMInstanceDao extends GenericDao, StateDao< */ List listDistinctHostNames(long networkId, VirtualMachine.Type... types); + List findByHostInStates(Long hostId, State... states); + + List listStartingWithNoHostId(); + } diff --git a/engine/schema/src/com/cloud/vm/dao/VMInstanceDaoImpl.java b/engine/schema/src/com/cloud/vm/dao/VMInstanceDaoImpl.java index 7198b7c24e0..ffb1a0b8b3d 100644 --- a/engine/schema/src/com/cloud/vm/dao/VMInstanceDaoImpl.java +++ b/engine/schema/src/com/cloud/vm/dao/VMInstanceDaoImpl.java @@ -5,7 +5,7 @@ // 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, @@ -83,30 +83,32 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem protected GenericSearchBuilder CountRunningByAccount; protected SearchBuilder NetworkTypeSearch; protected GenericSearchBuilder DistinctHostNameSearch; - + protected SearchBuilder HostAndStateSearch; + protected SearchBuilder StartingWithNoHostSearch; + @Inject ResourceTagDao _tagsDao; @Inject NicDao _nicDao; - + protected Attribute _updateTimeAttr; - - private static final String ORDER_CLUSTERS_NUMBER_OF_VMS_FOR_ACCOUNT_PART1 = + + private static final String ORDER_CLUSTERS_NUMBER_OF_VMS_FOR_ACCOUNT_PART1 = "SELECT host.cluster_id, SUM(IF(vm.state='Running' AND vm.account_id = ?, 1, 0)) FROM `cloud`.`host` host LEFT JOIN `cloud`.`vm_instance` vm ON host.id = vm.host_id WHERE "; private static final String ORDER_CLUSTERS_NUMBER_OF_VMS_FOR_ACCOUNT_PART2 = " AND host.type = 'Routing' GROUP BY host.cluster_id ORDER BY 2 ASC "; - + private static final String ORDER_PODS_NUMBER_OF_VMS_FOR_ACCOUNT = "SELECT pod.id, SUM(IF(vm.state='Running' AND vm.account_id = ?, 1, 0)) FROM `cloud`.`host_pod_ref` pod LEFT JOIN `cloud`.`vm_instance` vm ON pod.id = vm.pod_id WHERE pod.data_center_id = ? " + " GROUP BY pod.id ORDER BY 2 ASC "; - + private static final String ORDER_HOSTS_NUMBER_OF_VMS_FOR_ACCOUNT = "SELECT host.id, SUM(IF(vm.state='Running' AND vm.account_id = ?, 1, 0)) FROM `cloud`.`host` host LEFT JOIN `cloud`.`vm_instance` vm ON host.id = vm.host_id WHERE host.data_center_id = ? " + " AND host.pod_id = ? AND host.cluster_id = ? AND host.type = 'Routing' " + " GROUP BY host.id ORDER BY 2 ASC "; @Inject protected HostDao _hostDao; - + public VMInstanceDaoImpl() { } - + @PostConstruct protected void init() { @@ -114,14 +116,14 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem IdStatesSearch.and("id", IdStatesSearch.entity().getId(), Op.EQ); IdStatesSearch.and("states", IdStatesSearch.entity().getState(), Op.IN); IdStatesSearch.done(); - + VMClusterSearch = createSearchBuilder(); SearchBuilder hostSearch = _hostDao.createSearchBuilder(); VMClusterSearch.join("hostSearch", hostSearch, hostSearch.entity().getId(), VMClusterSearch.entity().getHostId(), JoinType.INNER); hostSearch.and("clusterId", hostSearch.entity().getClusterId(), SearchCriteria.Op.EQ); VMClusterSearch.done(); - + LHVMClusterSearch = createSearchBuilder(); SearchBuilder hostSearch1 = _hostDao.createSearchBuilder(); LHVMClusterSearch.join("hostSearch1", hostSearch1, hostSearch1.entity().getId(), LHVMClusterSearch.entity().getLastHostId(), JoinType.INNER); @@ -129,7 +131,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem hostSearch1.and("clusterId", hostSearch1.entity().getClusterId(), SearchCriteria.Op.EQ); LHVMClusterSearch.done(); - + AllFieldsSearch = createSearchBuilder(); AllFieldsSearch.and("host", AllFieldsSearch.entity().getHostId(), Op.EQ); AllFieldsSearch.and("lastHost", AllFieldsSearch.entity().getLastHostId(), Op.EQ); @@ -169,23 +171,23 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem IdTypesSearch.and("id", IdTypesSearch.entity().getId(), Op.EQ); IdTypesSearch.and("types", IdTypesSearch.entity().getType(), Op.IN); IdTypesSearch.done(); - + HostIdTypesSearch = createSearchBuilder(); HostIdTypesSearch.and("hostid", HostIdTypesSearch.entity().getHostId(), Op.EQ); HostIdTypesSearch.and("types", HostIdTypesSearch.entity().getType(), Op.IN); HostIdTypesSearch.done(); - + HostIdUpTypesSearch = createSearchBuilder(); HostIdUpTypesSearch.and("hostid", HostIdUpTypesSearch.entity().getHostId(), Op.EQ); HostIdUpTypesSearch.and("types", HostIdUpTypesSearch.entity().getType(), Op.IN); HostIdUpTypesSearch.and("states", HostIdUpTypesSearch.entity().getState(), Op.NIN); HostIdUpTypesSearch.done(); - + HostUpSearch = createSearchBuilder(); HostUpSearch.and("host", HostUpSearch.entity().getHostId(), Op.EQ); HostUpSearch.and("states", HostUpSearch.entity().getState(), Op.IN); HostUpSearch.done(); - + InstanceNameSearch = createSearchBuilder(); InstanceNameSearch.and("instanceName", InstanceNameSearch.entity().getInstanceName(), Op.EQ); InstanceNameSearch.done(); @@ -194,21 +196,31 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem CountVirtualRoutersByAccount.select(null, Func.COUNT, null); CountVirtualRoutersByAccount.and("account", CountVirtualRoutersByAccount.entity().getAccountId(), SearchCriteria.Op.EQ); CountVirtualRoutersByAccount.and("type", CountVirtualRoutersByAccount.entity().getType(), SearchCriteria.Op.EQ); - CountVirtualRoutersByAccount.and("state", CountVirtualRoutersByAccount.entity().getState(), SearchCriteria.Op.NIN); + CountVirtualRoutersByAccount.and("state", CountVirtualRoutersByAccount.entity().getState(), SearchCriteria.Op.NIN); CountVirtualRoutersByAccount.done(); - + CountRunningByHost = createSearchBuilder(Long.class); CountRunningByHost.select(null, Func.COUNT, null); CountRunningByHost.and("host", CountRunningByHost.entity().getHostId(), SearchCriteria.Op.EQ); CountRunningByHost.and("state", CountRunningByHost.entity().getState(), SearchCriteria.Op.EQ); - CountRunningByHost.done(); + CountRunningByHost.done(); CountRunningByAccount = createSearchBuilder(Long.class); CountRunningByAccount.select(null, Func.COUNT, null); CountRunningByAccount.and("account", CountRunningByAccount.entity().getAccountId(), SearchCriteria.Op.EQ); CountRunningByAccount.and("state", CountRunningByAccount.entity().getState(), SearchCriteria.Op.EQ); - CountRunningByAccount.done(); - + CountRunningByAccount.done(); + + HostAndStateSearch = createSearchBuilder(); + HostAndStateSearch.and("host", HostAndStateSearch.entity().getHostId(), Op.EQ); + HostAndStateSearch.and("states", HostAndStateSearch.entity().getState(), Op.IN); + HostAndStateSearch.done(); + + StartingWithNoHostSearch = createSearchBuilder(); + StartingWithNoHostSearch.and("state", StartingWithNoHostSearch.entity().getState(), Op.EQ); + StartingWithNoHostSearch.and("host", StartingWithNoHostSearch.entity().getHostId(), Op.NULL); + StartingWithNoHostSearch.done(); + _updateTimeAttr = _allAttributes.get("updateTime"); assert _updateTimeAttr != null : "Couldn't get this updateTime attribute"; } @@ -219,7 +231,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem sc.setParameters("account", accountId); return listBy(sc); } - + @Override public List findVMInstancesLike(String name) { SearchCriteria sc = NameLikeSearch.create(); @@ -234,7 +246,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem return listBy(sc); } - + @Override public List listByZoneId(long zoneId) { SearchCriteria sc = AllFieldsSearch.create(); @@ -242,7 +254,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem return listBy(sc); } - + @Override public List listByPodId(long podId) { SearchCriteria sc = AllFieldsSearch.create(); @@ -263,7 +275,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem sc.setJoinParameters("hostSearch1", "clusterId", clusterId); return listBy(sc); } - + @Override public List listByZoneIdAndType(long zoneId, VirtualMachine.Type type) { SearchCriteria sc = AllFieldsSearch.create(); @@ -271,8 +283,8 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem sc.setParameters("type", type.toString()); return listBy(sc); } - - + + @Override public List listNonExpungedByZoneAndTemplate(long zoneId, long templateId) { SearchCriteria sc = ZoneTemplateNonExpungedSearch.create(); @@ -310,7 +322,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem sc.setParameters("states", new Object[] {State.Destroyed, State.Stopped, State.Expunging}); return listBy(sc); } - + @Override public List listUpByHostId(Long hostId) { SearchCriteria sc = HostUpSearch.create(); @@ -318,14 +330,14 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem sc.setParameters("states", new Object[] {State.Starting, State.Running}); return listBy(sc); } - + @Override public List listByTypes(Type... types) { SearchCriteria sc = TypesSearch.create(); sc.setParameters("types", (Object[]) types); return listBy(sc); } - + @Override public List listByTypeAndState(VirtualMachine.Type type, State state) { SearchCriteria sc = AllFieldsSearch.create(); @@ -348,7 +360,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem sc.setParameters("instanceName", name); return findOneBy(sc); } - + @Override public void updateProxyId(long id, Long proxyId, Date time) { VMInstanceVO vo = createForUpdate(); @@ -369,12 +381,12 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem @SuppressWarnings("unchecked") Pair hosts = (Pair)opaque; Long newHostId = hosts.second(); - + VMInstanceVO vmi = (VMInstanceVO)vm; Long oldHostId = vmi.getHostId(); Long oldUpdated = vmi.getUpdated(); Date oldUpdateDate = vmi.getUpdateTime(); - + SearchCriteria sc = StateChangeSearch.create(); sc.setParameters("id", vmi.getId()); sc.setParameters("states", oldState); @@ -383,7 +395,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem vmi.incrUpdated(); UpdateBuilder ub = getUpdateBuilder(vmi); - + ub.set(vmi, "state", newState); ub.set(vmi, "hostId", newHostId); ub.set(vmi, "podIdToDeployIn", vmi.getPodIdToDeployIn()); @@ -393,7 +405,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem if (result == 0 && s_logger.isDebugEnabled()) { VMInstanceVO vo = findByIdIncludingRemoved(vm.getId()); - + if (vo != null) { StringBuilder str = new StringBuilder("Unable to update ").append(vo.toString()); str.append(": DB Data={Host=").append(vo.getHostId()).append("; State=").append(vo.getState().toString()).append("; updated=").append(vo.getUpdated()).append("; time=").append(vo.getUpdateTime()); @@ -407,7 +419,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem } return result > 0; } - + @Override public List listByLastHostId(Long hostId) { SearchCriteria sc = AllFieldsSearch.create(); @@ -415,7 +427,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem sc.setParameters("state", State.Stopped); return listBy(sc); } - + @Override public Long countAllocatedVirtualRoutersForAccount(long accountId) { SearchCriteria sc = CountVirtualRoutersByAccount.create(); @@ -424,7 +436,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem sc.setParameters("state", new Object[] {State.Destroyed, State.Error, State.Expunging}); return customSearch(sc, null).get(0); } - + @Override public List listVmsMigratingFromHost(Long hostId) { SearchCriteria sc = AllFieldsSearch.create(); @@ -432,7 +444,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem sc.setParameters("state", State.Migrating); return listBy(sc); } - + @Override public Long countRunningByHostId(long hostId){ SearchCriteria sc = CountRunningByHost.create(); @@ -455,7 +467,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem pstmt = txn.prepareAutoCloseStatement(sql.toString()); pstmt.setLong(1, accountId); pstmt.setLong(2, zoneId); - + ResultSet rs = pstmt.executeQuery(); while (rs.next()) { Long clusterId = rs.getLong(1); @@ -484,11 +496,11 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem pstmt = txn.prepareAutoCloseStatement(sql.toString()); pstmt.setLong(1, accountId); pstmt.setLong(2, podId); - + ResultSet rs = pstmt.executeQuery(); while (rs.next()) { Long clusterId = rs.getLong(1); - result.add(clusterId); + result.add(clusterId); clusterVmCountMap.put(clusterId, rs.getDouble(2)); } return new Pair, Map>(result, clusterVmCountMap); @@ -511,11 +523,11 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem pstmt = txn.prepareAutoCloseStatement(sql); pstmt.setLong(1, accountId); pstmt.setLong(2, dataCenterId); - + ResultSet rs = pstmt.executeQuery(); while (rs.next()) { Long podId = rs.getLong(1); - result.add(podId); + result.add(podId); podVmCountMap.put(podId, rs.getDouble(2)); } return new Pair, Map>(result, podVmCountMap); @@ -523,7 +535,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem throw new CloudRuntimeException("DB Exception on: " + ORDER_PODS_NUMBER_OF_VMS_FOR_ACCOUNT, e); } catch (Throwable e) { throw new CloudRuntimeException("Caught: " + ORDER_PODS_NUMBER_OF_VMS_FOR_ACCOUNT, e); - } + } } @Override @@ -538,7 +550,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem pstmt.setLong(2, dcId); pstmt.setLong(3, podId); pstmt.setLong(4, clusterId); - + ResultSet rs = pstmt.executeQuery(); while (rs.next()) { result.add(rs.getLong(1)); @@ -548,9 +560,9 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem throw new CloudRuntimeException("DB Exception on: " + ORDER_PODS_NUMBER_OF_VMS_FOR_ACCOUNT, e); } catch (Throwable e) { throw new CloudRuntimeException("Caught: " + ORDER_PODS_NUMBER_OF_VMS_FOR_ACCOUNT, e); - } + } } - + @Override public Long countRunningByAccount(long accountId){ SearchCriteria sc = CountRunningByAccount.create(); @@ -558,18 +570,18 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem sc.setParameters("state", State.Running); return customSearch(sc, null).get(0); } - + @Override public List listNonRemovedVmsByTypeAndNetwork(long networkId, VirtualMachine.Type... types) { if (NetworkTypeSearch == null) { - + SearchBuilder nicSearch = _nicDao.createSearchBuilder(); nicSearch.and("networkId", nicSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); NetworkTypeSearch = createSearchBuilder(); NetworkTypeSearch.and("types", NetworkTypeSearch.entity().getType(), SearchCriteria.Op.IN); NetworkTypeSearch.and("removed", NetworkTypeSearch.entity().getRemoved(), SearchCriteria.Op.NULL); - NetworkTypeSearch.join("nicSearch", nicSearch, NetworkTypeSearch.entity().getId(), + NetworkTypeSearch.join("nicSearch", nicSearch, NetworkTypeSearch.entity().getId(), nicSearch.entity().getInstanceId(), JoinBuilder.JoinType.INNER); NetworkTypeSearch.done(); } @@ -577,27 +589,27 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem SearchCriteria sc = NetworkTypeSearch.create(); if (types != null && types.length != 0) { sc.setParameters("types", (Object[]) types); - } + } sc.setJoinParameters("nicSearch", "networkId", networkId); return listBy(sc); } - - - + + + @Override public List listDistinctHostNames(long networkId, VirtualMachine.Type... types) { if (DistinctHostNameSearch == null) { - + SearchBuilder nicSearch = _nicDao.createSearchBuilder(); nicSearch.and("networkId", nicSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); DistinctHostNameSearch = createSearchBuilder(String.class); DistinctHostNameSearch.selectField(DistinctHostNameSearch.entity().getHostName()); - + DistinctHostNameSearch.and("types", DistinctHostNameSearch.entity().getType(), SearchCriteria.Op.IN); DistinctHostNameSearch.and("removed", DistinctHostNameSearch.entity().getRemoved(), SearchCriteria.Op.NULL); - DistinctHostNameSearch.join("nicSearch", nicSearch, DistinctHostNameSearch.entity().getId(), + DistinctHostNameSearch.join("nicSearch", nicSearch, DistinctHostNameSearch.entity().getId(), nicSearch.entity().getInstanceId(), JoinBuilder.JoinType.INNER); DistinctHostNameSearch.done(); } @@ -605,12 +617,12 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem SearchCriteria sc = DistinctHostNameSearch.create(); if (types != null && types.length != 0) { sc.setParameters("types", (Object[]) types); - } + } sc.setJoinParameters("nicSearch", "networkId", networkId); return customSearch(sc, null); } - + @Override @DB public boolean remove(Long id) { @@ -625,4 +637,19 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem return result; } + @Override + public List findByHostInStates(Long hostId, State... states) { + SearchCriteria sc = HostAndStateSearch.create(); + sc.setParameters("host", hostId); + sc.setParameters("states", (Object[]) states); + return listBy(sc); + } + + @Override + public List listStartingWithNoHostId() { + SearchCriteria sc = StartingWithNoHostSearch.create(); + sc.setParameters("state", State.Starting); + return listBy(sc); + } + } diff --git a/engine/storage/src/org/apache/cloudstack/storage/allocator/ClusterScopeStoragePoolAllocator.java b/engine/storage/src/org/apache/cloudstack/storage/allocator/ClusterScopeStoragePoolAllocator.java index 0dd55d1d325..5b1f8cd699a 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/allocator/ClusterScopeStoragePoolAllocator.java +++ b/engine/storage/src/org/apache/cloudstack/storage/allocator/ClusterScopeStoragePoolAllocator.java @@ -50,7 +50,7 @@ public class ClusterScopeStoragePoolAllocator extends AbstractStoragePoolAllocat @Override protected List select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo) { - + s_logger.debug("ClusterScopeStoragePoolAllocator looking for storage pool"); List suitablePools = new ArrayList(); @@ -65,6 +65,14 @@ public class ClusterScopeStoragePoolAllocator extends AbstractStoragePoolAllocat } List pools = _storagePoolDao.findPoolsByTags(dcId, podId, clusterId, dskCh.getTags()); + + // add remaining pools in cluster, that did not match tags, to avoid set + List allPools = _storagePoolDao.findPoolsByTags(dcId, podId, clusterId, null); + allPools.removeAll(pools); + for (StoragePoolVO pool : allPools) { + avoid.addPool(pool.getId()); + } + if (pools.size() == 0) { if (s_logger.isDebugEnabled()) { String storageType = dskCh.useLocalStorage() ? ServiceOffering.StorageType.local.toString() : ServiceOffering.StorageType.shared.toString(); @@ -72,7 +80,7 @@ public class ClusterScopeStoragePoolAllocator extends AbstractStoragePoolAllocat } return suitablePools; } - + for (StoragePoolVO pool: pools) { if(suitablePools.size() == returnUpTo){ break; @@ -80,13 +88,15 @@ public class ClusterScopeStoragePoolAllocator extends AbstractStoragePoolAllocat StoragePool pol = (StoragePool)this.dataStoreMgr.getPrimaryDataStore(pool.getId()); if (filter(avoid, pol, dskCh, plan)) { suitablePools.add(pol); + } else { + avoid.addPool(pool.getId()); } } - + if (s_logger.isDebugEnabled()) { s_logger.debug("FirstFitStoragePoolAllocator returning "+suitablePools.size() +" suitable storage pools"); } - + return suitablePools; } diff --git a/engine/storage/src/org/apache/cloudstack/storage/allocator/LocalStoragePoolAllocator.java b/engine/storage/src/org/apache/cloudstack/storage/allocator/LocalStoragePoolAllocator.java index 7447d988a58..632ba439cb0 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/allocator/LocalStoragePoolAllocator.java +++ b/engine/storage/src/org/apache/cloudstack/storage/allocator/LocalStoragePoolAllocator.java @@ -74,7 +74,7 @@ public class LocalStoragePoolAllocator extends AbstractStoragePoolAllocator { if (!dskCh.useLocalStorage()) { return suitablePools; } - + // data disk and host identified from deploying vm (attach volume case) if (dskCh.getType() == Volume.Type.DATADISK && plan.getHostId() != null) { List hostPools = _poolHostDao.listByHostId(plan.getHostId()); @@ -85,7 +85,9 @@ public class LocalStoragePoolAllocator extends AbstractStoragePoolAllocator { if (filter(avoid, pol, dskCh, plan)) { s_logger.debug("Found suitable local storage pool " + pool.getId() + ", adding to list"); suitablePools.add(pol); - } + } else { + avoid.addPool(pool.getId()); + } } if (suitablePools.size() == returnUpTo) { @@ -101,8 +103,19 @@ public class LocalStoragePoolAllocator extends AbstractStoragePoolAllocator { StoragePool pol = (StoragePool)this.dataStoreMgr.getPrimaryDataStore(pool.getId()); if (filter(avoid, pol, dskCh, plan)) { suitablePools.add(pol); - } + } else { + avoid.addPool(pool.getId()); + } } + + // add remaining pools in cluster, that did not match tags, to avoid + // set + List allPools = _storagePoolDao.findLocalStoragePoolsByTags(plan.getDataCenterId(), + plan.getPodId(), plan.getClusterId(), null); + allPools.removeAll(availablePools); + for (StoragePoolVO pool : allPools) { + avoid.addPool(pool.getId()); + } } if (s_logger.isDebugEnabled()) { @@ -111,7 +124,7 @@ public class LocalStoragePoolAllocator extends AbstractStoragePoolAllocator { return suitablePools; } - + @Override public boolean configure(String name, Map params) throws ConfigurationException { super.configure(name, params); diff --git a/engine/storage/src/org/apache/cloudstack/storage/allocator/ZoneWideStoragePoolAllocator.java b/engine/storage/src/org/apache/cloudstack/storage/allocator/ZoneWideStoragePoolAllocator.java index 1d3cd819d70..e9769802a37 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/allocator/ZoneWideStoragePoolAllocator.java +++ b/engine/storage/src/org/apache/cloudstack/storage/allocator/ZoneWideStoragePoolAllocator.java @@ -39,18 +39,18 @@ import com.cloud.vm.VirtualMachineProfile; @Component public class ZoneWideStoragePoolAllocator extends AbstractStoragePoolAllocator { private static final Logger s_logger = Logger.getLogger(ZoneWideStoragePoolAllocator.class); - @Inject PrimaryDataStoreDao _storagePoolDao; - @Inject DataStoreManager dataStoreMgr; - + @Inject PrimaryDataStoreDao _storagePoolDao; + @Inject DataStoreManager dataStoreMgr; + @Override - protected boolean filter(ExcludeList avoid, StoragePool pool, DiskProfile dskCh, + protected boolean filter(ExcludeList avoid, StoragePool pool, DiskProfile dskCh, DeploymentPlan plan) { Volume volume = _volumeDao.findById(dskCh.getVolumeId()); List requestVolumes = new ArrayList(); requestVolumes.add(volume); return storageMgr.storagePoolHasEnoughSpace(requestVolumes, pool); } - + @Override protected List select(DiskProfile dskCh, VirtualMachineProfile vmProfile, @@ -64,9 +64,16 @@ public class ZoneWideStoragePoolAllocator extends AbstractStoragePoolAllocator { return suitablePools; } } - + List storagePools = _storagePoolDao.findZoneWideStoragePoolsByTags(plan.getDataCenterId(), dskCh.getTags()); - + + // add remaining pools in zone, that did not match tags, to avoid set + List allPools = _storagePoolDao.findZoneWideStoragePoolsByTags(plan.getDataCenterId(), null); + allPools.removeAll(storagePools); + for (StoragePoolVO pool : allPools) { + avoid.addPool(pool.getId()); + } + for (StoragePoolVO storage : storagePools) { if (suitablePools.size() == returnUpTo) { break; @@ -74,7 +81,9 @@ public class ZoneWideStoragePoolAllocator extends AbstractStoragePoolAllocator { StoragePool pol = (StoragePool)this.dataStoreMgr.getPrimaryDataStore(storage.getId()); if (filter(avoid, pol, dskCh, plan)) { suitablePools.add(pol); - } + } else { + avoid.addPool(pol.getId()); + } } return suitablePools; } diff --git a/plugins/deployment-planners/user-concentrated-pod/src/com/cloud/deploy/UserConcentratedPodPlanner.java b/plugins/deployment-planners/user-concentrated-pod/src/com/cloud/deploy/UserConcentratedPodPlanner.java index 2ab98566e0a..d917893719e 100644 --- a/plugins/deployment-planners/user-concentrated-pod/src/com/cloud/deploy/UserConcentratedPodPlanner.java +++ b/plugins/deployment-planners/user-concentrated-pod/src/com/cloud/deploy/UserConcentratedPodPlanner.java @@ -11,7 +11,7 @@ // 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 +// KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package com.cloud.deploy; @@ -24,18 +24,17 @@ import javax.ejb.Local; import org.apache.log4j.Logger; -import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.utils.Pair; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; @Local(value=DeploymentPlanner.class) -public class UserConcentratedPodPlanner extends FirstFitPlanner implements DeploymentPlanner { +public class UserConcentratedPodPlanner extends FirstFitPlanner implements DeploymentClusterPlanner { private static final Logger s_logger = Logger.getLogger(UserConcentratedPodPlanner.class); - + /** - * This method should reorder the given list of Cluster Ids by applying any necessary heuristic + * This method should reorder the given list of Cluster Ids by applying any necessary heuristic * for this planner * For UserConcentratedPodPlanner we need to order the clusters in a zone across pods, by considering those pods first which have more number of VMs for this account * This reordering is not done incase the clusters within single pod are passed when the allocation is applied at pod-level. @@ -49,7 +48,7 @@ public class UserConcentratedPodPlanner extends FirstFitPlanner implements Deplo } return applyUserConcentrationPodHeuristicToClusters(id, clusterIdsByCapacity, vmProfile.getOwner().getAccountId()); } - + private List applyUserConcentrationPodHeuristicToClusters(long zoneId, List prioritizedClusterIds, long accountId){ //user has VMs in certain pods. - prioritize those pods first //UserConcentratedPod strategy @@ -61,8 +60,8 @@ public class UserConcentratedPodPlanner extends FirstFitPlanner implements Deplo clusterList = prioritizedClusterIds; } return clusterList; - } - + } + private List reorderClustersByPods(List clusterIds, List podIds) { if (s_logger.isDebugEnabled()) { @@ -111,11 +110,11 @@ public class UserConcentratedPodPlanner extends FirstFitPlanner implements Deplo return prioritizedPods; } - + /** - * This method should reorder the given list of Pod Ids by applying any necessary heuristic + * This method should reorder the given list of Pod Ids by applying any necessary heuristic * for this planner - * For UserConcentratedPodPlanner we need to order the pods by considering those pods first which have more number of VMs for this account + * For UserConcentratedPodPlanner we need to order the pods by considering those pods first which have more number of VMs for this account * @return List ordered list of Pod Ids */ @Override @@ -124,7 +123,7 @@ public class UserConcentratedPodPlanner extends FirstFitPlanner implements Deplo if(vmProfile.getOwner() == null){ return podIdsByCapacity; } - long accountId = vmProfile.getOwner().getAccountId(); + long accountId = vmProfile.getOwner().getAccountId(); //user has VMs in certain pods. - prioritize those pods first //UserConcentratedPod strategy @@ -138,18 +137,7 @@ public class UserConcentratedPodPlanner extends FirstFitPlanner implements Deplo }else{ return podIdsByCapacity; } - - } - @Override - public boolean canHandle(VirtualMachineProfile vm, DeploymentPlan plan, ExcludeList avoid) { - if(vm.getHypervisorType() != HypervisorType.BareMetal){ - //check the allocation strategy - if (_allocationAlgorithm != null && (_allocationAlgorithm.equals(AllocationAlgorithm.userconcentratedpod_random.toString()) || _allocationAlgorithm.equals(AllocationAlgorithm.userconcentratedpod_firstfit.toString()))){ - return true; - } - } - return false; } } diff --git a/plugins/deployment-planners/user-dispersing/src/com/cloud/deploy/UserDispersingPlanner.java b/plugins/deployment-planners/user-dispersing/src/com/cloud/deploy/UserDispersingPlanner.java index 2db2051389d..2b0b1588802 100755 --- a/plugins/deployment-planners/user-dispersing/src/com/cloud/deploy/UserDispersingPlanner.java +++ b/plugins/deployment-planners/user-dispersing/src/com/cloud/deploy/UserDispersingPlanner.java @@ -29,14 +29,13 @@ import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import com.cloud.configuration.Config; -import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; @Local(value=DeploymentPlanner.class) -public class UserDispersingPlanner extends FirstFitPlanner implements DeploymentPlanner { +public class UserDispersingPlanner extends FirstFitPlanner implements DeploymentClusterPlanner { private static final Logger s_logger = Logger.getLogger(UserDispersingPlanner.class); @@ -191,17 +190,6 @@ public class UserDispersingPlanner extends FirstFitPlanner implements Deployment } - @Override - public boolean canHandle(VirtualMachineProfile vm, DeploymentPlan plan, ExcludeList avoid) { - if(vm.getHypervisorType() != HypervisorType.BareMetal){ - //check the allocation strategy - if (_allocationAlgorithm != null && _allocationAlgorithm.equals(AllocationAlgorithm.userdispersing.toString())) { - return true; - } - } - return false; - } - float _userDispersionWeight; diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalPlannerSelector.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalPlannerSelector.java deleted file mode 100755 index 45fbeb782ab..00000000000 --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalPlannerSelector.java +++ /dev/null @@ -1,39 +0,0 @@ -// 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.baremetal.manager; - -import java.util.Map; - -import javax.ejb.Local; -import javax.naming.ConfigurationException; - -import com.cloud.deploy.AbstractDeployPlannerSelector; -import com.cloud.deploy.DeployPlannerSelector; -import com.cloud.hypervisor.Hypervisor.HypervisorType; -import com.cloud.vm.UserVmVO; -@Local(value = {DeployPlannerSelector.class}) -public class BaremetalPlannerSelector extends AbstractDeployPlannerSelector{ - - @Override - public String selectPlanner(UserVmVO vm) { - if (vm.getHypervisorType() == HypervisorType.BareMetal) { - return "BareMetalPlanner"; - } - return null; - } - -} diff --git a/server/pom.xml b/server/pom.xml index 004d9c8e068..6385bf2f233 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -90,6 +90,11 @@ cloud-api ${project.version} + + org.apache.cloudstack + cloud-framework-ipc + ${project.version} + org.apache.cloudstack cloud-framework-events diff --git a/server/src/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java b/server/src/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java index b54b1c1f527..b6286aab8da 100755 --- a/server/src/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java +++ b/server/src/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java @@ -78,7 +78,7 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { @Inject ConsoleProxyDao _consoleProxyDao = null; @Inject SecondaryStorageVmDao _secStorgaeVmDao = null; @Inject ConfigurationDao _configDao = null; - @Inject GuestOSDao _guestOSDao = null; + @Inject GuestOSDao _guestOSDao = null; @Inject GuestOSCategoryDao _guestOSCategoryDao = null; @Inject VMInstanceDao _vmInstanceDao = null; @Inject ResourceManager _resourceMgr; @@ -88,17 +88,17 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { boolean _checkHvm = true; protected String _allocationAlgorithm = "random"; @Inject CapacityManager _capacityMgr; - - + + @Override public List allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, int returnUpTo) { return allocateTo(vmProfile, plan, type, avoid, returnUpTo, true); } - + @Override public List allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, int returnUpTo, boolean considerReservedCapacity) { - + long dcId = plan.getDataCenterId(); Long podId = plan.getPodId(); Long clusterId = plan.getClusterId(); @@ -110,19 +110,19 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { // FirstFitAllocator should be used for user VMs only since it won't care whether the host is capable of routing or not return new ArrayList(); } - + if(s_logger.isDebugEnabled()){ s_logger.debug("Looking for hosts in dc: " + dcId + " pod:" + podId + " cluster:" + clusterId ); } - + String hostTagOnOffering = offering.getHostTag(); String hostTagOnTemplate = template.getTemplateTag(); - + boolean hasSvcOfferingTag = hostTagOnOffering != null ? true : false; boolean hasTemplateTag = hostTagOnTemplate != null ? true : false; - + List clusterHosts = new ArrayList(); - + String haVmTag = (String)vmProfile.getParameter(VirtualMachineProfile.Param.HaTag); if (haVmTag != null) { clusterHosts = _hostDao.listByHostTag(type, clusterId, podId, dcId, haVmTag); @@ -133,31 +133,31 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { List hostsMatchingOfferingTag = new ArrayList(); List hostsMatchingTemplateTag = new ArrayList(); if (hasSvcOfferingTag){ - if (s_logger.isDebugEnabled()){ + if (s_logger.isDebugEnabled()){ s_logger.debug("Looking for hosts having tag specified on SvcOffering:" + hostTagOnOffering); } hostsMatchingOfferingTag = _hostDao.listByHostTag(type, clusterId, podId, dcId, hostTagOnOffering); - if (s_logger.isDebugEnabled()){ + if (s_logger.isDebugEnabled()){ s_logger.debug("Hosts with tag '" + hostTagOnOffering + "' are:" + hostsMatchingOfferingTag); - } + } } if (hasTemplateTag){ - if (s_logger.isDebugEnabled()){ + if (s_logger.isDebugEnabled()){ s_logger.debug("Looking for hosts having tag specified on Template:" + hostTagOnTemplate); } - hostsMatchingTemplateTag = _hostDao.listByHostTag(type, clusterId, podId, dcId, hostTagOnTemplate); - if (s_logger.isDebugEnabled()){ + hostsMatchingTemplateTag = _hostDao.listByHostTag(type, clusterId, podId, dcId, hostTagOnTemplate); + if (s_logger.isDebugEnabled()){ s_logger.debug("Hosts with tag '" + hostTagOnTemplate+"' are:" + hostsMatchingTemplateTag); - } + } } - + if (hasSvcOfferingTag && hasTemplateTag){ hostsMatchingOfferingTag.retainAll(hostsMatchingTemplateTag); - clusterHosts = _hostDao.listByHostTag(type, clusterId, podId, dcId, hostTagOnTemplate); - if (s_logger.isDebugEnabled()){ + clusterHosts = _hostDao.listByHostTag(type, clusterId, podId, dcId, hostTagOnTemplate); + if (s_logger.isDebugEnabled()){ s_logger.debug("Found "+ hostsMatchingOfferingTag.size() +" Hosts satisfying both tags, host ids are:" + hostsMatchingOfferingTag); } - + clusterHosts = hostsMatchingOfferingTag; } else { if (hasSvcOfferingTag){ @@ -168,7 +168,14 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { } } } - + + // add all hosts that we are not considering to the avoid list + List allhostsInCluster = _hostDao.listAllUpAndEnabledNonHAHosts(type, clusterId, podId, dcId, null); + allhostsInCluster.removeAll(clusterHosts); + for (HostVO host : allhostsInCluster) { + avoid.addHost(host.getId()); + } + return allocateTo(plan, offering, template, avoid, clusterHosts, returnUpTo, considerReservedCapacity, account); } @@ -226,11 +233,11 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { }else if(_allocationAlgorithm.equals("userdispersing")){ hosts = reorderHostsByNumberOfVms(plan, hosts, account); } - + if (s_logger.isDebugEnabled()) { s_logger.debug("FirstFitAllocator has " + hosts.size() + " hosts to check for allocation: "+hosts); } - + // We will try to reorder the host lists such that we give priority to hosts that have // the minimums to support a VM's requirements hosts = prioritizeHosts(template, hosts); @@ -242,7 +249,7 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { if (s_logger.isDebugEnabled()) { s_logger.debug("Looking for speed=" + (offering.getCpu() * offering.getSpeed()) + "Mhz, Ram=" + offering.getRamSize()); } - + List suitableHosts = new ArrayList(); for (HostVO host : hosts) { @@ -255,7 +262,7 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { } continue; } - + //find number of guest VMs occupying capacity on this host. if (_capacityMgr.checkIfHostReachMaxGuestLimit(host)){ if (s_logger.isDebugEnabled()) { @@ -285,13 +292,14 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { if (s_logger.isDebugEnabled()) { s_logger.debug("Not using host " + host.getId() + "; numCpusGood: " + numCpusGood + "; cpuFreqGood: " + cpuFreqGood + ", host has capacity?" + hostHasCapacity); } + avoid.addHost(host.getId()); } } - + if (s_logger.isDebugEnabled()) { s_logger.debug("Host Allocator returning "+suitableHosts.size() +" suitable hosts"); } - + return suitableHosts; } @@ -302,26 +310,26 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { long dcId = plan.getDataCenterId(); Long podId = plan.getPodId(); Long clusterId = plan.getClusterId(); - + List hostIdsByVmCount = _vmInstanceDao.listHostIdsByVmCount(dcId, podId, clusterId, account.getAccountId()); if (s_logger.isDebugEnabled()) { s_logger.debug("List of hosts in ascending order of number of VMs: "+ hostIdsByVmCount); } - + //now filter the given list of Hosts by this ordered list - Map hostMap = new HashMap(); + Map hostMap = new HashMap(); for (HostVO host : hosts) { hostMap.put(host.getId(), host); } List matchingHostIds = new ArrayList(hostMap.keySet()); - + hostIdsByVmCount.retainAll(matchingHostIds); - + List reorderedHosts = new ArrayList(); for(Long id: hostIdsByVmCount){ reorderedHosts.add(hostMap.get(id)); } - + return reorderedHosts; } @@ -336,13 +344,13 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { if (template == null) { return hosts; } - + // Determine the guest OS category of the template String templateGuestOSCategory = getTemplateGuestOSCategory(template); - + List prioritizedHosts = new ArrayList(); List noHvmHosts = new ArrayList(); - + // If a template requires HVM and a host doesn't support HVM, remove it from consideration List hostsToCheck = new ArrayList(); if (template.isRequiresHvm()) { @@ -356,7 +364,7 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { } else { hostsToCheck.addAll(hosts); } - + if (s_logger.isDebugEnabled()) { if (noHvmHosts.size() > 0) { s_logger.debug("Not considering hosts: " + noHvmHosts + " to deploy template: " + template +" as they are not HVM enabled"); @@ -376,10 +384,10 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { lowPriorityHosts.add(host); } } - + hostsToCheck.removeAll(highPriorityHosts); hostsToCheck.removeAll(lowPriorityHosts); - + // Prioritize the remaining hosts by HVM capability for (HostVO host : hostsToCheck) { if (!template.isRequiresHvm() && !hostSupportsHVM(host)) { @@ -390,21 +398,21 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { prioritizedHosts.add(host); } } - + // Merge the lists prioritizedHosts.addAll(0, highPriorityHosts); prioritizedHosts.addAll(lowPriorityHosts); - + return prioritizedHosts; } - + protected boolean hostSupportsHVM(HostVO host) { if ( !_checkHvm ) { return true; } // Determine host capabilities String caps = host.getCapabilities(); - + if (caps != null) { String[] tokens = caps.split(","); for (String token : tokens) { @@ -413,24 +421,24 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { } } } - + return false; } - + protected String getHostGuestOSCategory(HostVO host) { DetailVO hostDetail = _hostDetailsDao.findDetail(host.getId(), "guest.os.category.id"); if (hostDetail != null) { String guestOSCategoryIdString = hostDetail.getValue(); long guestOSCategoryId; - + try { guestOSCategoryId = Long.parseLong(guestOSCategoryIdString); } catch (Exception e) { return null; } - + GuestOSCategoryVO guestOSCategory = _guestOSCategoryDao.findById(guestOSCategoryId); - + if (guestOSCategory != null) { return guestOSCategory.getName(); } else { @@ -440,7 +448,7 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { return null; } } - + protected String getTemplateGuestOSCategory(VMTemplateVO template) { long guestOSId = template.getGuestOSId(); GuestOSVO guestOS = _guestOSDao.findById(guestOSId); @@ -455,7 +463,7 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { Map configs = _configDao.getConfiguration(params); String opFactor = configs.get("cpu.overprovisioning.factor"); _factor = NumbersUtil.parseFloat(opFactor, 1); - + String allocationAlgorithm = configs.get("vm.allocation.algorithm"); if (allocationAlgorithm != null) { _allocationAlgorithm = allocationAlgorithm; diff --git a/server/src/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java index 9795fef66fd..ce20562d5f7 100644 --- a/server/src/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java @@ -73,6 +73,7 @@ public class ServiceOfferingJoinDaoImpl extends GenericDaoBase params) throws ConfigurationException { _vmCapacityReleaseInterval = NumbersUtil.parseInt(_configDao.getValue(Config.CapacitySkipcountingHours.key()), 3600); @@ -552,6 +564,20 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager, ServiceOffering so = offeringsMap.get(vm.getServiceOfferingId()); reservedMemory += so.getRamSize() * 1024L * 1024L; reservedCpu += so.getCpu() * so.getSpeed(); + } else { + // signal if not done already, that the VM has been stopped for skip.counting.hours, + // hence capacity will not be reserved anymore. + UserVmDetailVO messageSentFlag = _userVmDetailsDao.findDetail(vm.getId(), MESSAGE_RESERVED_CAPACITY_FREED_FLAG); + if (messageSentFlag == null || !Boolean.valueOf(messageSentFlag.getValue())) { + _messageBus.publish(_name, "VM_ReservedCapacity_Free", PublishScope.LOCAL, vm); + + if (vm.getType() == VirtualMachine.Type.User) { + UserVmVO userVM = _userVMDao.findById(vm.getId()); + _userVMDao.loadDetails(userVM); + userVM.setDetail(MESSAGE_RESERVED_CAPACITY_FREED_FLAG, "true"); + _userVMDao.saveDetails(userVM); + } + } } } @@ -688,6 +714,18 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager, allocateVmCapacity(vm, fromLastHost); } + if (newState == State.Stopped) { + if (vm.getType() == VirtualMachine.Type.User) { + + UserVmVO userVM = _userVMDao.findById(vm.getId()); + _userVMDao.loadDetails(userVM); + // free the message sent flag if it exists + userVM.setDetail(MESSAGE_RESERVED_CAPACITY_FREED_FLAG, "false"); + _userVMDao.saveDetails(userVM); + + } + } + return true; } diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 77ca2de1923..e1d3751f290 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -214,6 +214,8 @@ public enum Config { SecStorageProxy("Advanced", AgentManager.class, String.class, "secstorage.proxy", null, "http proxy used by ssvm, in http://username:password@proxyserver:port format", null), AlertPurgeInterval("Advanced", ManagementServer.class, Integer.class, "alert.purge.interval", "86400", "The interval (in seconds) to wait before running the alert purge thread", null), AlertPurgeDelay("Advanced", ManagementServer.class, Integer.class, "alert.purge.delay", "0", "Alerts older than specified number days will be purged. Set this value to 0 to never delete alerts", null), + HostReservationReleasePeriod("Advanced", ManagementServer.class, Integer.class, "host.reservation.release.period", "300000", "The interval in milliseconds between host reservation release checks", null), + // LB HealthCheck Interval. LBHealthCheck("Advanced", ManagementServer.class, String.class, "healthcheck.update.interval", "600", @@ -235,6 +237,7 @@ public enum Config { ApplyAllocationAlgorithmToPods("Advanced", ManagementServer.class, Boolean.class, "apply.allocation.algorithm.to.pods", "false", "If true, deployment planner applies the allocation heuristics at pods first in the given datacenter during VM resource allocation", "true,false"), VmUserDispersionWeight("Advanced", ManagementServer.class, Float.class, "vm.user.dispersion.weight", "1", "Weight for user dispersion heuristic (as a value between 0 and 1) applied to resource allocation during vm deployment. Weight for capacity heuristic will be (1 - weight of user dispersion)", null), VmAllocationAlgorithm("Advanced", ManagementServer.class, String.class, "vm.allocation.algorithm", "random", "'random', 'firstfit', 'userdispersing', 'userconcentratedpod_random', 'userconcentratedpod_firstfit' : Order in which hosts within a cluster will be considered for VM/volume allocation.", null), + VmDeploymentPlanner("Advanced", ManagementServer.class, String.class, "vm.deployment.planner", "FirstFitPlanner", "'FirstFitPlanner', 'UserDispersingPlanner', 'UserConcentratedPodPlanner': DeploymentPlanner heuristic that will be used for VM deployment.", null), EndpointeUrl("Advanced", ManagementServer.class, String.class, "endpointe.url", "http://localhost:8080/client/api", "Endpointe Url", null), ElasticLoadBalancerEnabled("Advanced", ManagementServer.class, String.class, "network.loadbalancer.basiczone.elb.enabled", "false", "Whether the load balancing service is enabled for basic zones", "true,false"), ElasticLoadBalancerNetwork("Advanced", ManagementServer.class, String.class, "network.loadbalancer.basiczone.elb.network", "guest", "Whether the elastic load balancing service public ips are taken from the public or guest network", "guest,public"), diff --git a/server/src/com/cloud/configuration/ConfigurationManager.java b/server/src/com/cloud/configuration/ConfigurationManager.java index 84ffc3e1aad..d0ae914c20f 100755 --- a/server/src/com/cloud/configuration/ConfigurationManager.java +++ b/server/src/com/cloud/configuration/ConfigurationManager.java @@ -79,10 +79,11 @@ public interface ConfigurationManager extends ConfigurationService, Manager { * TODO * @param id * @param useVirtualNetwork + * @param deploymentPlanner * @return ID */ ServiceOfferingVO createServiceOffering(long userId, boolean isSystem, VirtualMachine.Type vm_typeType, String name, int cpu, int ramSize, int speed, String displayText, boolean localStorageRequired, - boolean offerHA, boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, Integer networkRate); + boolean offerHA, boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, Integer networkRate, String deploymentPlanner); /** * Creates a new disk offering diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 887878254b3..9e0c847ed57 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -5,7 +5,7 @@ // 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, @@ -162,6 +162,7 @@ import com.cloud.org.Grouping.AllocationState; import com.cloud.projects.Project; import com.cloud.projects.ProjectManager; import com.cloud.server.ConfigurationServer; +import com.cloud.server.ManagementService; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.DiskOfferingVO; @@ -302,7 +303,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Inject AlertManager _alertMgr; // @com.cloud.utils.component.Inject(adapter = SecurityChecker.class) - @Inject + @Inject List _secChecker; @Inject @@ -346,6 +347,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Inject NicIpAliasDao _nicIpAliasDao; + @Inject + public ManagementService _mgr; + // FIXME - why don't we have interface for DataCenterLinkLocalIpAddressDao? @Inject protected DataCenterLinkLocalIpAddressDao _LinkLocalIpAllocDao; @@ -356,11 +360,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Override public boolean configure(final String name, final Map params) throws ConfigurationException { String maxVolumeSizeInGbString = _configDao.getValue(Config.MaxVolumeSize.key()); - _maxVolumeSizeInGb = NumbersUtil.parseInt(maxVolumeSizeInGbString, + _maxVolumeSizeInGb = NumbersUtil.parseInt(maxVolumeSizeInGbString, Integer.parseInt(Config.MaxVolumeSize.getDefaultValue())); String defaultPageSizeString = _configDao.getValue(Config.DefaultPageSize.key()); - _defaultPageSize = NumbersUtil.parseLong(defaultPageSizeString, + _defaultPageSize = NumbersUtil.parseLong(defaultPageSizeString, Long.parseLong(Config.DefaultPageSize.getDefaultValue())); populateConfigValuesForValidationSet(); @@ -920,7 +924,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati checkPodCidrSubnets(zoneId, podId, cidr); /* * Commenting out due to Bug 11593 - CIDR conflicts with zone when extending pod but not when creating it - * + * * checkCidrVlanOverlap(zoneId, cidr); */ } @@ -1713,7 +1717,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (internalDns2 == null) { internalDns2 = zone.getInternalDns2(); } - + if (guestCidr == null) { guestCidr = zone.getGuestNetworkCidr(); } @@ -2034,17 +2038,29 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new InvalidParameterValueException("Network rate can be specified only for non-System offering and system offerings having \"domainrouter\" systemvmtype"); } + if (cmd.getDeploymentPlanner() != null) { + List planners = _mgr.listDeploymentPlanners(); + if (planners != null && !planners.isEmpty()) { + if (!planners.contains(cmd.getDeploymentPlanner())) { + throw new InvalidParameterValueException( + "Invalid name for Deployment Planner specified, please use listDeploymentPlanners to get the valid set"); + } + } else { + throw new InvalidParameterValueException("No deployment planners found"); + } + } + return createServiceOffering(userId, cmd.getIsSystem(), vmType, cmd.getServiceOfferingName(), cpuNumber.intValue(), memory.intValue(), cpuSpeed.intValue(), cmd.getDisplayText(), - localStorageRequired, offerHA, limitCpuUse, volatileVm, cmd.getTags(), cmd.getDomainId(), cmd.getHostTag(), cmd.getNetworkRate()); + localStorageRequired, offerHA, limitCpuUse, volatileVm, cmd.getTags(), cmd.getDomainId(), cmd.getHostTag(), cmd.getNetworkRate(), cmd.getDeploymentPlanner()); } @Override @ActionEvent(eventType = EventTypes.EVENT_SERVICE_OFFERING_CREATE, eventDescription = "creating service offering") public ServiceOfferingVO createServiceOffering(long userId, boolean isSystem, VirtualMachine.Type vm_type, String name, int cpu, int ramSize, int speed, String displayText, - boolean localStorageRequired, boolean offerHA, boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, Integer networkRate) { + boolean localStorageRequired, boolean offerHA, boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, Integer networkRate, String deploymentPlanner) { tags = cleanupTags(tags); ServiceOfferingVO offering = new ServiceOfferingVO(name, cpu, ramSize, speed, networkRate, null, offerHA, limitResourceUse, volatileVm, displayText, localStorageRequired, false, tags, isSystem, vm_type, - domainId, hostTag); + domainId, hostTag, deploymentPlanner); if ((offering = _serviceOfferingDao.persist(offering)) != null) { UserContext.current().setEventDetails("Service offering id=" + offering.getId()); @@ -2328,9 +2344,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati String endIPv6 = cmd.getEndIpv6(); String ip6Gateway = cmd.getIp6Gateway(); String ip6Cidr = cmd.getIp6Cidr(); - + Account vlanOwner = null; - + boolean ipv4 = (startIP != null); boolean ipv6 = (startIPv6 != null); @@ -2387,7 +2403,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } else if (ipv6) { throw new InvalidParameterValueException("Only support IPv6 on extending existed network"); } - + // Verify that zone exists DataCenterVO zone = _zoneDao.findById(zoneId); if (zone == null) { @@ -2434,18 +2450,18 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } } - - + + // Check if zone is enabled Account caller = UserContext.current().getCaller(); if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) { throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: " + zoneId); - } + } if (zone.isSecurityGroupEnabled() && zone.getNetworkType() != DataCenter.NetworkType.Basic && forVirtualNetwork) { throw new InvalidParameterValueException("Can't add virtual ip range into a zone with security group enabled"); } - + // If networkId is not specified, and vlan is Virtual or Direct Untagged, try to locate default networks if (forVirtualNetwork) { if (network == null) { @@ -2604,35 +2620,35 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati String startIP, String endIP, String vlanGateway, String vlanNetmask, String vlanId, Account vlanOwner, String startIPv6, String endIPv6, String vlanIp6Gateway, String vlanIp6Cidr) { Network network = _networkModel.getNetwork(networkId); - + boolean ipv4 = false, ipv6 = false; - + if (startIP != null) { ipv4 = true; } - + if (startIPv6 != null) { ipv6 = true; } - + if (!ipv4 && !ipv6) { throw new InvalidParameterValueException("Please specify IPv4 or IPv6 address."); } - + //Validate the zone DataCenterVO zone = _zoneDao.findById(zoneId); if (zone == null) { throw new InvalidParameterValueException("Please specify a valid zone."); } - + // ACL check checkZoneAccess(UserContext.current().getCaller(), zone); - + //Validate the physical network if (_physicalNetworkDao.findById(physicalNetworkId) == null) { throw new InvalidParameterValueException("Please specify a valid physical network id"); } - + //Validate the pod if (podId != null) { Pod pod = _podDao.findById(podId); @@ -2644,11 +2660,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } //pod vlans can be created in basic zone only if (zone.getNetworkType() != NetworkType.Basic || network.getTrafficType() != TrafficType.Guest) { - throw new InvalidParameterValueException("Pod id can be specified only for the networks of type " - + TrafficType.Guest + " in zone of type " + NetworkType.Basic); + throw new InvalidParameterValueException("Pod id can be specified only for the networks of type " + + TrafficType.Guest + " in zone of type " + NetworkType.Basic); } } - + //1) if vlan is specified for the guest network range, it should be the same as network's vlan //2) if vlan is missing, default it to the guest network's vlan if (network.getTrafficType() == TrafficType.Guest) { @@ -2660,7 +2676,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati //For pvlan networkVlanId = networkVlanId.split("-")[0]; } - + if (vlanId != null) { // if vlan is specified, throw an error if it's not equal to network's vlanId if (networkVlanId != null && !networkVlanId.equalsIgnoreCase(vlanId)) { @@ -2673,14 +2689,14 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati //vlan id is required for public network throw new InvalidParameterValueException("Vlan id is required when add ip range to the public network"); } - + if (vlanId == null) { vlanId = Vlan.UNTAGGED; } VlanType vlanType = forVirtualNetwork ? VlanType.VirtualNetwork : VlanType.DirectAttached; - - + + if (vlanOwner != null && zone.getNetworkType() != NetworkType.Advanced) { throw new InvalidParameterValueException("Vlan owner can be defined only in the zone of type " + NetworkType.Advanced); } @@ -2696,7 +2712,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new InvalidParameterValueException("Please specify a valid netmask"); } } - + if (ipv6) { if (!NetUtils.isValidIpv6(vlanIp6Gateway)) { throw new InvalidParameterValueException("Please specify a valid IPv6 gateway"); @@ -2751,7 +2767,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati List vlans = _vlanDao.listByZone(zone.getId()); for (VlanVO vlan : vlans) { String otherVlanGateway = vlan.getVlanGateway(); - // Continue if it's not IPv4 + // Continue if it's not IPv4 if (otherVlanGateway == null) { continue; } @@ -2787,14 +2803,14 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } } - + String ipv6Range = null; if (ipv6) { ipv6Range = startIPv6; if (endIPv6 != null) { ipv6Range += "-" + endIPv6; } - + List vlans = _vlanDao.listByZone(zone.getId()); for (VlanVO vlan : vlans) { if (vlan.getIp6Gateway() == null) { @@ -2820,14 +2836,14 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } String ipRange = null; - + if (ipv4) { ipRange = startIP; if (endIP != null) { ipRange += "-" + endIP; } } - + // Everything was fine, so persist the VLAN Transaction txn = Transaction.currentTxn(); txn.start(); @@ -2839,7 +2855,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // IPv6 use a used ip map, is different from ipv4, no need to save public ip range if (ipv4) { if (!savePublicIPRange(startIP, endIP, zoneId, vlan.getId(), networkId, physicalNetworkId)) { - throw new CloudRuntimeException("Failed to save IPv4 range. Please contact Cloud Support."); + throw new CloudRuntimeException("Failed to save IPv4 range. Please contact Cloud Support."); } } @@ -2875,7 +2891,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (vlanRange == null) { throw new InvalidParameterValueException("Please specify a valid IP range id."); } - + boolean isAccountSpecific = false; List acctVln = _accountVlanMapDao.listAccountVlanMapsByVlan(vlanRange.getId()); // Check for account wide pool. It will have an entry for account_vlan_map. @@ -2888,7 +2904,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati List ips = _publicIpAddressDao.listByVlanId(vlanDbId); boolean success = true; if (allocIpCount > 0) { - if (isAccountSpecific) { + if (isAccountSpecific) { try { vlanRange = _vlanDao.acquireInLockTable(vlanDbId, 30); if (vlanRange == null) { @@ -2901,7 +2917,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati for (IPAddressVO ip : ips) { if (ip.isOneToOneNat()) { - throw new InvalidParameterValueException("Can't delete account specific vlan " + vlanDbId + + throw new InvalidParameterValueException("Can't delete account specific vlan " + vlanDbId + " as ip " + ip + " belonging to the range is used for static nat purposes. Cleanup the rules first"); } @@ -2910,9 +2926,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati " as ip " + ip + " belonging to the range is a source nat ip for the network id=" + ip.getSourceNetworkId() + ". IP range with the source nat ip address can be removed either as a part of Network, or account removal"); } - + if (_firewallDao.countRulesByIpId(ip.getId()) > 0) { - throw new InvalidParameterValueException("Can't delete account specific vlan " + vlanDbId + + throw new InvalidParameterValueException("Can't delete account specific vlan " + vlanDbId + " as ip " + ip + " belonging to the range has firewall rules applied. Cleanup the rules first"); } //release public ip address here @@ -3268,7 +3284,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati return true; } - + @DB protected boolean savePublicIPRange(String startIP, String endIP, long zoneId, long vlanDbId, long sourceNetworkid, long physicalNetworkId) { @@ -3471,7 +3487,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } - + private boolean validPod(long podId) { return (_podDao.findById(podId) != null); } @@ -3690,7 +3706,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (provider == Provider.JuniperSRX || provider == Provider.CiscoVnmc) { firewallProvider = provider; } - + if ((service == Service.PortForwarding || service == Service.StaticNat) && provider == Provider.VirtualRouter){ firewallProvider = Provider.VirtualRouter; } @@ -3890,7 +3906,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (!specifyVlan && type == GuestType.Shared) { throw new InvalidParameterValueException("SpecifyVlan should be true if network offering's type is " + type); } - + //specifyIpRanges should always be true for Shared networks //specifyIpRanges can only be true for Isolated networks with no Source Nat service if (specifyIpRanges) { @@ -3914,7 +3930,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (availability == NetworkOffering.Availability.Required) { boolean canOffBeRequired = (type == GuestType.Isolated && serviceProviderMap.containsKey(Service.SourceNat)); if (!canOffBeRequired) { - throw new InvalidParameterValueException("Availability can be " + NetworkOffering.Availability.Required + throw new InvalidParameterValueException("Availability can be " + NetworkOffering.Availability.Required + " only for networkOfferings of type " + GuestType.Isolated + " and with " + Service.SourceNat.getName() + " enabled"); } @@ -3922,11 +3938,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // only one network offering in the system can be Required List offerings = _networkOfferingDao.listByAvailability(Availability.Required, false); if (!offerings.isEmpty()) { - throw new InvalidParameterValueException("System already has network offering id=" + offerings.get(0).getId() + throw new InvalidParameterValueException("System already has network offering id=" + offerings.get(0).getId() + " with availability " + Availability.Required); } } - + boolean dedicatedLb = false; boolean elasticLb = false; boolean sharedSourceNat = false; @@ -3938,7 +3954,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati boolean internalLb = false; if (serviceCapabilityMap != null && !serviceCapabilityMap.isEmpty()) { Map lbServiceCapabilityMap = serviceCapabilityMap.get(Service.Lb); - + if ((lbServiceCapabilityMap != null) && (!lbServiceCapabilityMap.isEmpty())) { String isolationCapability = lbServiceCapabilityMap.get(Capability.SupportedLBIsolation); if (isolationCapability != null) { @@ -3952,7 +3968,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (param != null) { elasticLb = param.contains("true"); } - + String inlineMode = lbServiceCapabilityMap.get(Capability.InlineMode); if (inlineMode != null) { _networkModel.checkCapabilityForProvider(serviceProviderMap.get(Service.Lb), Service.Lb, Capability.InlineMode, inlineMode); @@ -3983,14 +3999,14 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if ((sourceNatServiceCapabilityMap != null) && (!sourceNatServiceCapabilityMap.isEmpty())) { String sourceNatType = sourceNatServiceCapabilityMap.get(Capability.SupportedSourceNatTypes); if (sourceNatType != null) { - _networkModel.checkCapabilityForProvider(serviceProviderMap.get(Service.SourceNat), Service.SourceNat, + _networkModel.checkCapabilityForProvider(serviceProviderMap.get(Service.SourceNat), Service.SourceNat, Capability.SupportedSourceNatTypes, sourceNatType); sharedSourceNat = sourceNatType.contains("perzone"); } String param = sourceNatServiceCapabilityMap.get(Capability.RedundantRouter); if (param != null) { - _networkModel.checkCapabilityForProvider(serviceProviderMap.get(Service.SourceNat), Service.SourceNat, + _networkModel.checkCapabilityForProvider(serviceProviderMap.get(Service.SourceNat), Service.SourceNat, Capability.RedundantRouter, param); redundantRouter = param.contains("true"); } @@ -4009,7 +4025,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } - NetworkOfferingVO offering = new NetworkOfferingVO(name, displayText, trafficType, systemOnly, specifyVlan, + NetworkOfferingVO offering = new NetworkOfferingVO(name, displayText, trafficType, systemOnly, specifyVlan, networkRate, multicastRate, isDefault, availability, tags, type, conserveMode, dedicatedLb, sharedSourceNat, redundantRouter, elasticIp, elasticLb, specifyIpRanges, inline, isPersistent, associatePublicIp, publicLb, internalLb); @@ -4041,7 +4057,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati _ntwkOffServiceMapDao.persist(offService); s_logger.trace("Added service for the network offering: " + offService + " with provider " + provider.getName()); } - + if (vpcOff) { List supportedSvcs = new ArrayList(); supportedSvcs.addAll(serviceProviderMap.keySet()); @@ -4251,7 +4267,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // filter by supported services boolean listBySupportedServices = (supportedServicesStr != null && !supportedServicesStr.isEmpty() && !offerings.isEmpty()); boolean checkIfProvidersAreEnabled = (zoneId != null); - boolean parseOfferings = (listBySupportedServices || sourceNatSupported != null || checkIfProvidersAreEnabled + boolean parseOfferings = (listBySupportedServices || sourceNatSupported != null || checkIfProvidersAreEnabled || forVpc != null || network != null); if (parseOfferings) { @@ -4299,7 +4315,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (sourceNatSupported != null) { addOffering = addOffering && (_networkModel.areServicesSupportedByNetworkOffering(offering.getId(), Network.Service.SourceNat) == sourceNatSupported); } - + if (forVpc != null) { addOffering = addOffering && (isOfferingForVpc(offering) == forVpc.booleanValue()); } else if (network != null){ @@ -4418,14 +4434,14 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } if (availability == null) { - throw new InvalidParameterValueException("Invalid value for Availability. Supported types: " + throw new InvalidParameterValueException("Invalid value for Availability. Supported types: " + Availability.Required + ", " + Availability.Optional); } else { if (availability == NetworkOffering.Availability.Required) { - boolean canOffBeRequired = (offeringToUpdate.getGuestType() == GuestType.Isolated + boolean canOffBeRequired = (offeringToUpdate.getGuestType() == GuestType.Isolated && _networkModel.areServicesSupportedByNetworkOffering(offeringToUpdate.getId(), Service.SourceNat)); if (!canOffBeRequired) { - throw new InvalidParameterValueException("Availability can be " + + throw new InvalidParameterValueException("Availability can be " + NetworkOffering.Availability.Required + " only for networkOfferings of type " + GuestType.Isolated + " and with " + Service.SourceNat.getName() + " enabled"); } @@ -4433,7 +4449,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // only one network offering in the system can be Required List offerings = _networkOfferingDao.listByAvailability(Availability.Required, false); if (!offerings.isEmpty() && offerings.get(0).getId() != offeringToUpdate.getId()) { - throw new InvalidParameterValueException("System already has network offering id=" + + throw new InvalidParameterValueException("System already has network offering id=" + offerings.get(0).getId() + " with availability " + Availability.Required); } } @@ -4452,7 +4468,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @ActionEvent(eventType = EventTypes.EVENT_ACCOUNT_MARK_DEFAULT_ZONE, eventDescription = "Marking account with the " + "default zone", async=true) public AccountVO markDefaultZone(String accountName, long domainId, long defaultZoneId) { - + // Check if the account exists Account account = _accountDao.findEnabledAccount(accountName, domainId); if (account == null) { @@ -4466,9 +4482,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } AccountVO acctForUpdate = _accountDao.findById(account.getId()); - + acctForUpdate.setDefaultZoneId(defaultZoneId); - + if (_accountDao.update(account.getId(), acctForUpdate)) { UserContext.current().setEventDetails("Default zone id= " + defaultZoneId); return _accountDao.findById(account.getId()); @@ -4476,7 +4492,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati return null; } } - + // Note: This method will be used for entity name validations in the coming // releases (place holder for now) private void validateEntityName(String str) { @@ -4604,10 +4620,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati public ClusterVO getCluster(long id) { return _clusterDao.findById(id); } - + @Override public AllocationState findClusterAllocationState(ClusterVO cluster){ - + if(cluster.getAllocationState() == AllocationState.Disabled){ return AllocationState.Disabled; }else if(ApiDBUtils.findPodById(cluster.getPodId()).getAllocationState() == AllocationState.Disabled){ @@ -4615,20 +4631,20 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati }else { DataCenterVO zone = ApiDBUtils.findZoneById(cluster.getDataCenterId()); return zone.getAllocationState(); - } - } + } + } @Override public AllocationState findPodAllocationState(HostPodVO pod){ - + if(pod.getAllocationState() == AllocationState.Disabled){ return AllocationState.Disabled; }else { DataCenterVO zone = ApiDBUtils.findZoneById(pod.getDataCenterId()); return zone.getAllocationState(); - } + } } - + private boolean allowIpRangeOverlap(VlanVO vlan, boolean forVirtualNetwork, long networkId) { // FIXME - delete restriction for virtual network in the future if (vlan.getVlanType() == VlanType.DirectAttached && !forVirtualNetwork) { diff --git a/server/src/com/cloud/deploy/AbstractDeployPlannerSelector.java b/server/src/com/cloud/deploy/AbstractDeployPlannerSelector.java deleted file mode 100755 index 7665687be60..00000000000 --- a/server/src/com/cloud/deploy/AbstractDeployPlannerSelector.java +++ /dev/null @@ -1,84 +0,0 @@ -// 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.Map; - -import javax.inject.Inject; -import javax.naming.ConfigurationException; - -import com.cloud.configuration.Config; -import com.cloud.configuration.dao.ConfigurationDao; -import com.cloud.utils.component.AdapterBase; -import com.cloud.vm.UserVmVO; - -public abstract class AbstractDeployPlannerSelector extends AdapterBase implements DeployPlannerSelector { - protected Map params; - protected String name; - protected int runLevel; - - @Inject - protected ConfigurationDao _configDao; - protected String _allocationAlgorithm = "random"; - - @Override - public String getName() { - return name; - } - - @Override - public void setName(String name) { - this.name = name; - } - - @Override - public void setConfigParams(Map params) { - this.params = params; - } - - @Override - public Map getConfigParams() { - return params; - } - - @Override - public int getRunLevel() { - return runLevel; - } - - @Override - public void setRunLevel(int level) { - this.runLevel = level; - } - - @Override - public boolean configure(String name, Map params) throws ConfigurationException { - super.configure(name, params); - _allocationAlgorithm = _configDao.getValue(Config.VmAllocationAlgorithm.key()); - return true; - } - - @Override - public boolean start() { - return true; - } - - @Override - public boolean stop() { - return true; - } -} diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java index c7162a2003f..c86d5e1a1b2 100644 --- a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -16,32 +16,107 @@ // under the License. package com.cloud.deploy; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.TreeSet; import javax.ejb.Local; import javax.inject.Inject; +import javax.naming.ConfigurationException; import org.apache.cloudstack.affinity.AffinityGroupProcessor; -import org.apache.cloudstack.affinity.AffinityGroupVMMapVO; + import org.apache.cloudstack.affinity.dao.AffinityGroupDao; import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; +import org.apache.cloudstack.framework.messagebus.MessageBus; +import org.apache.cloudstack.framework.messagebus.MessageSubscriber; + +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.log4j.Logger; + +import com.cloud.capacity.CapacityManager; +import com.cloud.capacity.dao.CapacityDao; +import com.cloud.cluster.ManagementServerNode; +import com.cloud.configuration.Config; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.dc.ClusterDetailsDao; +import com.cloud.dc.ClusterDetailsVO; +import com.cloud.dc.ClusterVO; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.Pod; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.HostPodDao; 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.Host; +import com.cloud.host.HostVO; +import com.cloud.host.Status; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.offering.ServiceOffering; +import com.cloud.org.Cluster; +import com.cloud.org.Grouping; +import com.cloud.resource.ResourceState; +import com.cloud.storage.DiskOfferingVO; +import com.cloud.storage.StorageManager; +import com.cloud.storage.StoragePool; +import com.cloud.storage.StoragePoolHostVO; +import com.cloud.storage.Volume; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.DiskOfferingDao; +import com.cloud.storage.dao.GuestOSCategoryDao; +import com.cloud.storage.dao.GuestOSDao; +import com.cloud.storage.dao.StoragePoolHostDao; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.user.AccountManager; +import com.cloud.utils.DateUtil; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.Pair; 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.DiskProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.VirtualMachine.State; 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; +import com.cloud.agent.manager.allocator.HostAllocator; + @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 +124,53 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy protected AffinityGroupDao _affinityGroupDao; @Inject protected AffinityGroupVMMapDao _affinityGroupVMMapDao; + @Inject + DataCenterDao _dcDao; + @Inject + PlannerHostReservationDao _plannerHostReserveDao; + private int _vmCapacityReleaseInterval; + @Inject + MessageBus _messageBus; + private Timer _timer = null; + private long _hostReservationReleasePeriod = 60L * 60L * 1000L; // one hour by default + + private static final long INITIAL_RESERVATION_RELEASE_CHECKER_DELAY = 30L * 1000L; // thirty seconds expressed in milliseconds + protected long _nodeId = -1; + + protected List _storagePoolAllocators; + public List getStoragePoolAllocators() { + return _storagePoolAllocators; + } + public void setStoragePoolAllocators( + List _storagePoolAllocators) { + this._storagePoolAllocators = _storagePoolAllocators; + } + + protected List _hostAllocators; + public List getHostAllocators() { + return _hostAllocators; + } + public void setHostAllocators(List _hostAllocators) { + this._hostAllocators = _hostAllocators; + } + + @Inject protected HostDao _hostDao; + @Inject protected HostPodDao _podDao; + @Inject protected ClusterDao _clusterDao; + @Inject protected GuestOSDao _guestOSDao = null; + @Inject protected GuestOSCategoryDao _guestOSCategoryDao = null; + @Inject protected DiskOfferingDao _diskOfferingDao; + @Inject protected StoragePoolHostDao _poolHostDao; + + @Inject protected VolumeDao _volsDao; + @Inject protected CapacityManager _capacityMgr; + @Inject protected ConfigurationDao _configDao; + @Inject protected PrimaryDataStoreDao _storagePoolDao; + @Inject protected CapacityDao _capacityDao; + @Inject protected AccountManager _accountMgr; + @Inject protected StorageManager _storageMgr; + @Inject DataStoreManager dataStoreMgr; + @Inject protected ClusterDetailsDao _clusterDetailsDao; protected List _planners; public List getPlanners() { @@ -87,20 +209,908 @@ 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; + DataCenter dc = _dcDao.findById(vm.getDataCenterId()); + // check if datacenter is in avoid set + if (avoids.shouldAvoid(dc)) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("DataCenter id = '" + dc.getId() + + "' provided is in avoid set, DeploymentPlanner cannot allocate the VM, returning."); } - if (dest != null) { - avoids.addHost(dest.getHost().getId()); + return null; + } + + + ServiceOffering offering = vmProfile.getServiceOffering(); + String plannerName = offering.getDeploymentPlanner(); + if (plannerName == null) { + if (vm.getHypervisorType() == HypervisorType.BareMetal) { + plannerName = "BareMetalPlanner"; + } else { + plannerName = _configDao.getValue(Config.VmDeploymentPlanner.key()); + } + } + DeploymentPlanner planner = null; + for (DeploymentPlanner plannerInList : _planners) { + if (plannerName.equals(plannerInList.getName())) { + planner = plannerInList; break; } - } + + int cpu_requested = offering.getCpu() * offering.getSpeed(); + long ram_requested = offering.getRamSize() * 1024L * 1024L; + + if (s_logger.isDebugEnabled()) { + s_logger.debug("DeploymentPlanner allocation algorithm: " + planner); + + s_logger.debug("Trying to allocate a host and storage pools from dc:" + plan.getDataCenterId() + ", pod:" + + plan.getPodId() + ",cluster:" + plan.getClusterId() + ", requested cpu: " + cpu_requested + + ", requested ram: " + ram_requested); + + s_logger.debug("Is ROOT volume READY (pool already allocated)?: " + + (plan.getPoolId() != null ? "Yes" : "No")); + } + + String haVmTag = (String) vmProfile.getParameter(VirtualMachineProfile.Param.HaTag); + + if (plan.getHostId() != null && haVmTag == null) { + Long hostIdSpecified = plan.getHostId(); + if (s_logger.isDebugEnabled()) { + s_logger.debug("DeploymentPlan has host_id specified, choosing this host and making no checks on this host: " + + hostIdSpecified); + } + HostVO host = _hostDao.findById(hostIdSpecified); + if (host == null) { + s_logger.debug("The specified host cannot be found"); + } else if (avoids.shouldAvoid(host)) { + s_logger.debug("The specified host is in avoid set"); + } else { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Looking for suitable pools for this host under zone: " + host.getDataCenterId() + + ", pod: " + host.getPodId() + ", cluster: " + host.getClusterId()); + } + + // search for storage under the zone, pod, cluster of the host. + DataCenterDeployment lastPlan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), + host.getClusterId(), hostIdSpecified, plan.getPoolId(), null, plan.getReservationContext()); + + Pair>, List> result = findSuitablePoolsForVolumes(vmProfile, + lastPlan, avoids, HostAllocator.RETURN_UPTO_ALL); + Map> suitableVolumeStoragePools = result.first(); + List readyAndReusedVolumes = result.second(); + + // choose the potential pool for this VM for this host + if (!suitableVolumeStoragePools.isEmpty()) { + List suitableHosts = new ArrayList(); + suitableHosts.add(host); + + Pair> potentialResources = findPotentialDeploymentResources( + suitableHosts, suitableVolumeStoragePools, avoids, getPlannerUsage(planner)); + if (potentialResources != null) { + Pod pod = _podDao.findById(host.getPodId()); + Cluster cluster = _clusterDao.findById(host.getClusterId()); + Map storageVolMap = potentialResources.second(); + // remove the reused vol<->pool from destination, since + // we don't have to prepare this volume. + for (Volume vol : readyAndReusedVolumes) { + storageVolMap.remove(vol); + } + DeployDestination dest = new DeployDestination(dc, pod, cluster, host, storageVolMap); + s_logger.debug("Returning Deployment Destination: " + dest); + return dest; + } + } + } + s_logger.debug("Cannnot deploy to specified host, returning."); + return null; + } + + if (vm.getLastHostId() != null && haVmTag == null) { + s_logger.debug("This VM has last host_id specified, trying to choose the same host: " + vm.getLastHostId()); + + HostVO host = _hostDao.findById(vm.getLastHostId()); + if (host == null) { + s_logger.debug("The last host of this VM cannot be found"); + } else if (avoids.shouldAvoid(host)) { + s_logger.debug("The last host of this VM is in avoid set"); + } else if (_capacityMgr.checkIfHostReachMaxGuestLimit(host)) { + s_logger.debug("The last Host, hostId: " + + host.getId() + + " already has max Running VMs(count includes system VMs), skipping this and trying other available hosts"); + } else { + if (host.getStatus() == Status.Up && host.getResourceState() == ResourceState.Enabled) { + long cluster_id = host.getClusterId(); + ClusterDetailsVO cluster_detail_cpu = _clusterDetailsDao.findDetail(cluster_id, + "cpuOvercommitRatio"); + ClusterDetailsVO cluster_detail_ram = _clusterDetailsDao.findDetail(cluster_id, + "memoryOvercommitRatio"); + Float cpuOvercommitRatio = Float.parseFloat(cluster_detail_cpu.getValue()); + Float memoryOvercommitRatio = Float.parseFloat(cluster_detail_ram.getValue()); + if (_capacityMgr.checkIfHostHasCapacity(host.getId(), cpu_requested, ram_requested, true, + cpuOvercommitRatio, memoryOvercommitRatio, true)) { + s_logger.debug("The last host of this VM is UP and has enough capacity"); + s_logger.debug("Now checking for suitable pools under zone: " + host.getDataCenterId() + + ", pod: " + host.getPodId() + ", cluster: " + host.getClusterId()); + // search for storage under the zone, pod, cluster of + // the last host. + DataCenterDeployment lastPlan = new DataCenterDeployment(host.getDataCenterId(), + host.getPodId(), host.getClusterId(), host.getId(), plan.getPoolId(), null); + Pair>, List> result = findSuitablePoolsForVolumes( + vmProfile, lastPlan, avoids, HostAllocator.RETURN_UPTO_ALL); + Map> suitableVolumeStoragePools = result.first(); + List readyAndReusedVolumes = result.second(); + // choose the potential pool for this VM for this host + if (!suitableVolumeStoragePools.isEmpty()) { + List suitableHosts = new ArrayList(); + suitableHosts.add(host); + + Pair> potentialResources = findPotentialDeploymentResources( + suitableHosts, suitableVolumeStoragePools, avoids, getPlannerUsage(planner)); + if (potentialResources != null) { + Pod pod = _podDao.findById(host.getPodId()); + Cluster cluster = _clusterDao.findById(host.getClusterId()); + Map storageVolMap = potentialResources.second(); + // remove the reused vol<->pool from + // destination, since we don't have to prepare + // this volume. + for (Volume vol : readyAndReusedVolumes) { + storageVolMap.remove(vol); + } + DeployDestination dest = new DeployDestination(dc, pod, cluster, host, storageVolMap); + s_logger.debug("Returning Deployment Destination: " + dest); + return dest; + } + } + } else { + s_logger.debug("The last host of this VM does not have enough capacity"); + } + } else { + s_logger.debug("The last host of this VM is not UP or is not enabled, host status is: " + + host.getStatus().name() + ", host resource state is: " + host.getResourceState()); + } + } + s_logger.debug("Cannot choose the last host to deploy this VM "); + } + + DeployDestination dest = null; + List clusterList = null; + + if (planner != null && planner.canHandle(vmProfile, plan, avoids)) { + while (true) { + + if (planner instanceof DeploymentClusterPlanner) { + + ExcludeList PlannerAvoidInput = new ExcludeList(avoids.getDataCentersToAvoid(), + avoids.getPodsToAvoid(), avoids.getClustersToAvoid(), avoids.getHostsToAvoid(), + avoids.getPoolsToAvoid()); + + clusterList = ((DeploymentClusterPlanner) planner).orderClusters(vmProfile, plan, avoids); + + if (clusterList != null && !clusterList.isEmpty()) { + // planner refactoring. call allocators to list hosts + ExcludeList PlannerAvoidOutput = new ExcludeList(avoids.getDataCentersToAvoid(), + avoids.getPodsToAvoid(), avoids.getClustersToAvoid(), avoids.getHostsToAvoid(), + avoids.getPoolsToAvoid()); + + resetAvoidSet(PlannerAvoidOutput, PlannerAvoidInput); + + dest = checkClustersforDestination(clusterList, vmProfile, plan, avoids, dc, + getPlannerUsage(planner), PlannerAvoidOutput); + if (dest != null) { + return dest; + } + // reset the avoid input to the planners + resetAvoidSet(avoids, PlannerAvoidOutput); + + } else { + return null; + } + } else { + dest = planner.plan(vmProfile, plan, avoids); + if (dest != null) { + long hostId = dest.getHost().getId(); + avoids.addHost(dest.getHost().getId()); + + if (checkIfHostFitsPlannerUsage(hostId, DeploymentPlanner.PlannerResourceUsage.Shared)) { + // found destination + return dest; + } else { + // find another host - seems some concurrent + // deployment picked it up for dedicated access + continue; + } + } else { + return null; + } + } + } + } + + return dest; } + private void resetAvoidSet(ExcludeList avoidSet, ExcludeList removeSet) { + if (avoidSet.getDataCentersToAvoid() != null && removeSet.getDataCentersToAvoid() != null) { + avoidSet.getDataCentersToAvoid().removeAll(removeSet.getDataCentersToAvoid()); + } + if (avoidSet.getPodsToAvoid() != null && removeSet.getPodsToAvoid() != null) { + avoidSet.getPodsToAvoid().removeAll(removeSet.getPodsToAvoid()); + } + if (avoidSet.getClustersToAvoid() != null && removeSet.getClustersToAvoid() != null) { + avoidSet.getClustersToAvoid().removeAll(removeSet.getClustersToAvoid()); + } + if (avoidSet.getHostsToAvoid() != null && removeSet.getHostsToAvoid() != null) { + avoidSet.getHostsToAvoid().removeAll(removeSet.getHostsToAvoid()); + } + if (avoidSet.getPoolsToAvoid() != null && removeSet.getPoolsToAvoid() != null) { + avoidSet.getPoolsToAvoid().removeAll(removeSet.getPoolsToAvoid()); + } + } + + private PlannerResourceUsage getPlannerUsage(DeploymentPlanner planner) { + if (planner != null && planner instanceof DeploymentClusterPlanner) { + return ((DeploymentClusterPlanner) planner).getResourceUsage(); + } else { + return DeploymentPlanner.PlannerResourceUsage.Shared; + } + + } + + @DB + private boolean checkIfHostFitsPlannerUsage(long hostId, PlannerResourceUsage resourceUsageRequired) { + // 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 == resourceUsageRequired) { + return true; + } else { + s_logger.debug("Cannot use this host for usage: " + resourceUsageRequired + + ", since this host has been reserved for planner usage : " + hostResourceType); + 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(resourceUsageRequired); + _plannerHostReserveDao.persist(lockedEntry); + return true; + } else { + // someone updated it earlier. check if we can still use it + if (lockedEntry.getResourceUsage() == resourceUsageRequired) { + return true; + } else { + s_logger.debug("Cannot use this host for usage: " + resourceUsageRequired + + ", since this host has been reserved for planner usage : " + hostResourceType); + return false; + } + } + } finally { + txn.commit(); + } + } + + } + + return false; + } + + @DB + public boolean checkHostReservationRelease(Long hostId) { + + if (hostId != null) { + PlannerHostReservationVO reservationEntry = _plannerHostReserveDao.findByHostId(hostId); + if (reservationEntry != null && reservationEntry.getResourceUsage() != null) { + + // check if any VMs are starting or running on this host + List vms = _vmInstanceDao.listUpByHostId(hostId); + if (vms.size() > 0) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Cannot release reservation, Found " + vms.size() + " VMs Running on host " + + hostId); + } + return false; + } + + List vmsByLastHostId = _vmInstanceDao.listByLastHostId(hostId); + if (vmsByLastHostId.size() > 0) { + // check if any VMs are within skip.counting.hours, if yes + // we + // cannot release the host + for (VMInstanceVO stoppedVM : vmsByLastHostId) { + long secondsSinceLastUpdate = (DateUtil.currentGMTTime().getTime() - stoppedVM.getUpdateTime() + .getTime()) / 1000; + if (secondsSinceLastUpdate < _vmCapacityReleaseInterval) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Cannot release reservation, Found VM: " + stoppedVM + + " Stopped but reserved on host " + hostId); + } + return false; + } + } + } + + // check if any VMs are stopping on or migrating to this host + List vmsStoppingMigratingByHostId = _vmInstanceDao.findByHostInStates(hostId, + State.Stopping, State.Migrating, State.Starting); + if (vmsStoppingMigratingByHostId.size() > 0) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Cannot release reservation, Found " + vms.size() + + " VMs stopping/migrating on host " + hostId); + } + return false; + } + + // check if any VMs are in starting state with no hostId set yet + // - + // just ignore host release to avoid race condition + List vmsStartingNoHost = _vmInstanceDao.listStartingWithNoHostId(); + + if (vmsStartingNoHost.size() > 0) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Cannot release reservation, Found " + vms.size() + + " VMs starting as of now and no hostId yet stored"); + } + return false; + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Host has no VMs associated, releasing the planner reservation for host " + hostId); + } + + long id = reservationEntry.getId(); + 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(null); + _plannerHostReserveDao.persist(lockedEntry); + return true; + } + } finally { + txn.commit(); + } + } + + } + return false; + } + + class HostReservationReleaseChecker extends TimerTask { + @Override + public void run() { + try { + s_logger.debug("Checking if any host reservation can be released ... "); + checkHostReservations(); + s_logger.debug("Done running HostReservationReleaseChecker ... "); + } catch (Throwable t) { + s_logger.error("Exception in HostReservationReleaseChecker", t); + } + } + } + + private void checkHostReservations() { + List reservedHosts = _plannerHostReserveDao.listAllReservedHosts(); + + for (PlannerHostReservationVO hostReservation : reservedHosts) { + HostVO host = _hostDao.findById(hostReservation.getHostId()); + if (host != null && host.getManagementServerId() != null && host.getManagementServerId() == _nodeId) { + checkHostReservationRelease(hostReservation.getHostId()); + } + } + + } + + @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); + _messageBus.subscribe("VM_ReservedCapacity_Free", new MessageSubscriber() { + @Override + public void onPublishMessage(String senderAddress, String subject, Object obj) { + VMInstanceVO vm = ((VMInstanceVO) obj); + s_logger.debug("MessageBus message: host reserved capacity released for VM: " + vm.getLastHostId() + + ", checking if host reservation can be released for host:" + vm.getLastHostId()); + Long hostId = vm.getLastHostId(); + checkHostReservationRelease(hostId); + } + }); + + _vmCapacityReleaseInterval = NumbersUtil.parseInt(_configDao.getValue(Config.CapacitySkipcountingHours.key()), + 3600); + + String hostReservationReleasePeriod = _configDao.getValue(Config.HostReservationReleasePeriod.key()); + if (hostReservationReleasePeriod != null) { + _hostReservationReleasePeriod = Long.parseLong(hostReservationReleasePeriod); + if (_hostReservationReleasePeriod <= 0) + _hostReservationReleasePeriod = Long.parseLong(Config.HostReservationReleasePeriod.getDefaultValue()); + } + + _timer = new Timer("HostReservationReleaseChecker"); + + _nodeId = ManagementServerNode.getManagementServerId(); + + return super.configure(name, params); + } + + @Override + public boolean start() { + _timer.schedule(new HostReservationReleaseChecker(), INITIAL_RESERVATION_RELEASE_CHECKER_DELAY, + _hostReservationReleasePeriod); + return true; + } + + @Override + public boolean stop() { + _timer.cancel(); + return true; + } + + // /refactoring planner methods + private DeployDestination checkClustersforDestination(List clusterList, + VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, + DataCenter dc, DeploymentPlanner.PlannerResourceUsage resourceUsageRequired, ExcludeList PlannerAvoidOutput) { + + if (s_logger.isTraceEnabled()) { + s_logger.trace("ClusterId List to consider: " + clusterList); + } + + for (Long clusterId : clusterList) { + ClusterVO clusterVO = _clusterDao.findById(clusterId); + + if (clusterVO.getHypervisorType() != vmProfile.getHypervisorType()) { + s_logger.debug("Cluster: " + clusterId + + " has HyperVisorType that does not match the VM, skipping this cluster"); + avoid.addCluster(clusterVO.getId()); + continue; + } + + s_logger.debug("Checking resources in Cluster: " + clusterId + " under Pod: " + clusterVO.getPodId()); + // search for resources(hosts and storage) under this zone, pod, + // cluster. + DataCenterDeployment potentialPlan = new DataCenterDeployment(plan.getDataCenterId(), clusterVO.getPodId(), + clusterVO.getId(), null, plan.getPoolId(), null, plan.getReservationContext()); + + // find suitable hosts under this cluster, need as many hosts as we + // get. + List suitableHosts = findSuitableHosts(vmProfile, potentialPlan, avoid, HostAllocator.RETURN_UPTO_ALL); + // if found suitable hosts in this cluster, find suitable storage + // pools for each volume of the VM + if (suitableHosts != null && !suitableHosts.isEmpty()) { + if (vmProfile.getHypervisorType() == HypervisorType.BareMetal) { + Pod pod = _podDao.findById(clusterVO.getPodId()); + DeployDestination dest = new DeployDestination(dc, pod, clusterVO, suitableHosts.get(0)); + return dest; + } + + Pair>, List> result = findSuitablePoolsForVolumes(vmProfile, + potentialPlan, avoid, StoragePoolAllocator.RETURN_UPTO_ALL); + Map> suitableVolumeStoragePools = result.first(); + List readyAndReusedVolumes = result.second(); + + // choose the potential host and pool for the VM + if (!suitableVolumeStoragePools.isEmpty()) { + Pair> potentialResources = findPotentialDeploymentResources( + suitableHosts, suitableVolumeStoragePools, avoid, resourceUsageRequired); + + if (potentialResources != null) { + Pod pod = _podDao.findById(clusterVO.getPodId()); + Host host = _hostDao.findById(potentialResources.first().getId()); + Map storageVolMap = potentialResources.second(); + // remove the reused vol<->pool from destination, since + // we don't have to prepare this volume. + for (Volume vol : readyAndReusedVolumes) { + storageVolMap.remove(vol); + } + DeployDestination dest = new DeployDestination(dc, pod, clusterVO, host, storageVolMap); + s_logger.debug("Returning Deployment Destination: " + dest); + return dest; + } + } else { + s_logger.debug("No suitable storagePools found under this Cluster: " + clusterId); + } + } else { + s_logger.debug("No suitable hosts found under this Cluster: " + clusterId); + } + + if (canAvoidCluster(clusterVO, avoid, PlannerAvoidOutput)) { + avoid.addCluster(clusterVO.getId()); + } + } + s_logger.debug("Could not find suitable Deployment Destination for this VM under any clusters, returning. "); + return null; + } + + private boolean canAvoidCluster(Cluster clusterVO, ExcludeList avoids, ExcludeList plannerAvoidOutput) { + + ExcludeList allocatorAvoidOutput = new ExcludeList(avoids.getDataCentersToAvoid(), avoids.getPodsToAvoid(), + avoids.getClustersToAvoid(), avoids.getHostsToAvoid(), avoids.getPoolsToAvoid()); + + // remove any hosts/pools that the planners might have added + // to get the list of hosts/pools that Allocators flagged as 'avoid' + if (allocatorAvoidOutput.getHostsToAvoid() != null && plannerAvoidOutput.getHostsToAvoid() != null) { + allocatorAvoidOutput.getHostsToAvoid().removeAll(plannerAvoidOutput.getHostsToAvoid()); + } + if (allocatorAvoidOutput.getPoolsToAvoid() != null && plannerAvoidOutput.getPoolsToAvoid() != null) { + allocatorAvoidOutput.getPoolsToAvoid().removeAll(plannerAvoidOutput.getPoolsToAvoid()); + } + + // if all hosts or all pools in the cluster are in avoid set after this + // pass, then put the cluster in avoid set. + boolean avoidAllHosts = true, avoidAllPools = true; + + List allhostsInCluster = _hostDao.listAllUpAndEnabledNonHAHosts(Host.Type.Routing, clusterVO.getId(), + clusterVO.getPodId(), clusterVO.getDataCenterId(), null); + for (HostVO host : allhostsInCluster) { + if (allocatorAvoidOutput.getHostsToAvoid() == null + || !allocatorAvoidOutput.getHostsToAvoid().contains(host.getId())) { + // there's some host in the cluster that is not yet in avoid set + avoidAllHosts = false; + } + } + + List allPoolsInCluster = _storagePoolDao.findPoolsByTags(clusterVO.getDataCenterId(), + clusterVO.getPodId(), clusterVO.getId(), null); + for (StoragePoolVO pool : allPoolsInCluster) { + if (allocatorAvoidOutput.getPoolsToAvoid() == null + || !allocatorAvoidOutput.getPoolsToAvoid().contains(pool.getId())) { + // there's some pool in the cluster that is not yet in avoid set + avoidAllPools = false; + } + } + + if (avoidAllHosts || avoidAllPools) { + return true; + } + return false; + } + + protected Pair> findPotentialDeploymentResources(List suitableHosts, + Map> suitableVolumeStoragePools, ExcludeList avoid, + DeploymentPlanner.PlannerResourceUsage resourceUsageRequired) { + s_logger.debug("Trying to find a potenial host and associated storage pools from the suitable host/pool lists for this VM"); + + boolean hostCanAccessPool = false; + boolean haveEnoughSpace = false; + Map storage = new HashMap(); + TreeSet volumesOrderBySizeDesc = new TreeSet(new Comparator() { + @Override + public int compare(Volume v1, Volume v2) { + if (v1.getSize() < v2.getSize()) + return 1; + else + return -1; + } + }); + volumesOrderBySizeDesc.addAll(suitableVolumeStoragePools.keySet()); + boolean multipleVolume = volumesOrderBySizeDesc.size() > 1; + for (Host potentialHost : suitableHosts) { + Map> volumeAllocationMap = new HashMap>(); + for (Volume vol : volumesOrderBySizeDesc) { + haveEnoughSpace = false; + s_logger.debug("Checking if host: " + potentialHost.getId() + + " can access any suitable storage pool for volume: " + vol.getVolumeType()); + List volumePoolList = suitableVolumeStoragePools.get(vol); + hostCanAccessPool = false; + for (StoragePool potentialSPool : volumePoolList) { + if (hostCanAccessSPool(potentialHost, potentialSPool)) { + hostCanAccessPool = true; + if (multipleVolume) { + List requestVolumes = null; + if (volumeAllocationMap.containsKey(potentialSPool)) + requestVolumes = volumeAllocationMap.get(potentialSPool); + else + requestVolumes = new ArrayList(); + requestVolumes.add(vol); + + if (!_storageMgr.storagePoolHasEnoughSpace(requestVolumes, potentialSPool)) + continue; + volumeAllocationMap.put(potentialSPool, requestVolumes); + } + storage.put(vol, potentialSPool); + haveEnoughSpace = true; + break; + } + } + if (!hostCanAccessPool) { + break; + } + if (!haveEnoughSpace) { + s_logger.warn("insufficient capacity to allocate all volumes"); + break; + } + } + if (hostCanAccessPool && haveEnoughSpace + && checkIfHostFitsPlannerUsage(potentialHost.getId(), resourceUsageRequired)) { + s_logger.debug("Found a potential host " + "id: " + potentialHost.getId() + " name: " + + potentialHost.getName() + " and associated storage pools for this VM"); + return new Pair>(potentialHost, storage); + } else { + avoid.addHost(potentialHost.getId()); + } + } + s_logger.debug("Could not find a potential host that has associated storage pools from the suitable host/pool lists for this VM"); + return null; + } + + protected boolean hostCanAccessSPool(Host host, StoragePool pool) { + boolean hostCanAccessSPool = false; + + StoragePoolHostVO hostPoolLinkage = _poolHostDao.findByPoolHost(pool.getId(), host.getId()); + if (hostPoolLinkage != null) { + hostCanAccessSPool = true; + } + + s_logger.debug("Host: " + host.getId() + (hostCanAccessSPool ? " can" : " cannot") + " access pool: " + + pool.getId()); + return hostCanAccessSPool; + } + + protected List findSuitableHosts(VirtualMachineProfile vmProfile, + DeploymentPlan plan, ExcludeList avoid, int returnUpTo) { + List suitableHosts = new ArrayList(); + for (HostAllocator allocator : _hostAllocators) { + suitableHosts = allocator.allocateTo(vmProfile, plan, Host.Type.Routing, avoid, returnUpTo); + if (suitableHosts != null && !suitableHosts.isEmpty()) { + break; + } + } + + if (suitableHosts.isEmpty()) { + s_logger.debug("No suitable hosts found"); + } + return suitableHosts; + } + + protected Pair>, List> findSuitablePoolsForVolumes( + VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, + int returnUpTo) { + List volumesTobeCreated = _volsDao.findUsableVolumesForInstance(vmProfile.getId()); + Map> suitableVolumeStoragePools = new HashMap>(); + List readyAndReusedVolumes = new ArrayList(); + + // for each volume find list of suitable storage pools by calling the + // allocators + for (VolumeVO toBeCreated : volumesTobeCreated) { + s_logger.debug("Checking suitable pools for volume (Id, Type): (" + toBeCreated.getId() + "," + + toBeCreated.getVolumeType().name() + ")"); + + // If the plan specifies a poolId, it means that this VM's ROOT + // volume is ready and the pool should be reused. + // In this case, also check if rest of the volumes are ready and can + // be reused. + if (plan.getPoolId() != null) { + s_logger.debug("Volume has pool already allocated, checking if pool can be reused, poolId: " + + toBeCreated.getPoolId()); + List suitablePools = new ArrayList(); + StoragePool pool = null; + if (toBeCreated.getPoolId() != null) { + pool = (StoragePool) this.dataStoreMgr.getPrimaryDataStore(toBeCreated.getPoolId()); + } else { + pool = (StoragePool) this.dataStoreMgr.getPrimaryDataStore(plan.getPoolId()); + } + + if (!pool.isInMaintenance()) { + if (!avoid.shouldAvoid(pool)) { + long exstPoolDcId = pool.getDataCenterId(); + + long exstPoolPodId = pool.getPodId() != null ? pool.getPodId() : -1; + long exstPoolClusterId = pool.getClusterId() != null ? pool.getClusterId() : -1; + if (plan.getDataCenterId() == exstPoolDcId && plan.getPodId() == exstPoolPodId + && plan.getClusterId() == exstPoolClusterId) { + s_logger.debug("Planner need not allocate a pool for this volume since its READY"); + suitablePools.add(pool); + suitableVolumeStoragePools.put(toBeCreated, suitablePools); + if (!(toBeCreated.getState() == Volume.State.Allocated || toBeCreated.getState() == Volume.State.Creating)) { + readyAndReusedVolumes.add(toBeCreated); + } + continue; + } else { + s_logger.debug("Pool of the volume does not fit the specified plan, need to reallocate a pool for this volume"); + } + } else { + s_logger.debug("Pool of the volume is in avoid set, need to reallocate a pool for this volume"); + } + } else { + s_logger.debug("Pool of the volume is in maintenance, need to reallocate a pool for this volume"); + } + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("We need to allocate new storagepool for this volume"); + } + if (!isRootAdmin(plan.getReservationContext())) { + if (!isEnabledForAllocation(plan.getDataCenterId(), plan.getPodId(), plan.getClusterId())) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Cannot allocate new storagepool for this volume in this cluster, allocation state is disabled"); + s_logger.debug("Cannot deploy to this specified plan, allocation state is disabled, returning."); + } + // Cannot find suitable storage pools under this cluster for + // this volume since allocation_state is disabled. + // - remove any suitable pools found for other volumes. + // All volumes should get suitable pools under this cluster; + // else we cant use this cluster. + suitableVolumeStoragePools.clear(); + break; + } + } + + s_logger.debug("Calling StoragePoolAllocators to find suitable pools"); + + DiskOfferingVO diskOffering = _diskOfferingDao.findById(toBeCreated.getDiskOfferingId()); + DiskProfile diskProfile = new DiskProfile(toBeCreated, diskOffering, vmProfile.getHypervisorType()); + + boolean useLocalStorage = false; + if (vmProfile.getType() != VirtualMachine.Type.User) { + String ssvmUseLocalStorage = _configDao.getValue(Config.SystemVMUseLocalStorage.key()); + if (ssvmUseLocalStorage.equalsIgnoreCase("true")) { + useLocalStorage = true; + } + } else { + useLocalStorage = diskOffering.getUseLocalStorage(); + + // TODO: this is a hacking fix for the problem of deploy + // ISO-based VM on local storage + // when deploying VM based on ISO, we have a service offering + // and an additional disk offering, use-local storage flag is + // actually + // saved in service offering, overrde the flag from service + // offering when it is a ROOT disk + if (!useLocalStorage && vmProfile.getServiceOffering().getUseLocalStorage()) { + if (toBeCreated.getVolumeType() == Volume.Type.ROOT) + useLocalStorage = true; + } + } + diskProfile.setUseLocalStorage(useLocalStorage); + + boolean foundPotentialPools = false; + for (StoragePoolAllocator allocator : _storagePoolAllocators) { + final List suitablePools = allocator.allocateToPool(diskProfile, vmProfile, plan, avoid, + returnUpTo); + if (suitablePools != null && !suitablePools.isEmpty()) { + suitableVolumeStoragePools.put(toBeCreated, suitablePools); + foundPotentialPools = true; + break; + } + } + + if (!foundPotentialPools) { + s_logger.debug("No suitable pools found for volume: " + toBeCreated + " under cluster: " + + plan.getClusterId()); + // No suitable storage pools found under this cluster for this + // volume. - remove any suitable pools found for other volumes. + // All volumes should get suitable pools under this cluster; + // else we cant use this cluster. + suitableVolumeStoragePools.clear(); + break; + } + } + + if (suitableVolumeStoragePools.isEmpty()) { + s_logger.debug("No suitable pools found"); + } + + return new Pair>, List>(suitableVolumeStoragePools, readyAndReusedVolumes); + } + + private boolean isEnabledForAllocation(long zoneId, Long podId, Long clusterId) { + // Check if the zone exists in the system + DataCenterVO zone = _dcDao.findById(zoneId); + if (zone != null && Grouping.AllocationState.Disabled == zone.getAllocationState()) { + s_logger.info("Zone is currently disabled, cannot allocate to this zone: " + zoneId); + return false; + } + + Pod pod = _podDao.findById(podId); + if (pod != null && Grouping.AllocationState.Disabled == pod.getAllocationState()) { + s_logger.info("Pod is currently disabled, cannot allocate to this pod: " + podId); + return false; + } + + Cluster cluster = _clusterDao.findById(clusterId); + if (cluster != null && Grouping.AllocationState.Disabled == cluster.getAllocationState()) { + s_logger.info("Cluster is currently disabled, cannot allocate to this cluster: " + clusterId); + return false; + } + + return true; + } + + private boolean isRootAdmin(ReservationContext reservationContext) { + if (reservationContext != null) { + if (reservationContext.getAccount() != null) { + return _accountMgr.isRootAdmin(reservationContext.getAccount().getType()); + } else { + return false; + } + } + return false; + } } diff --git a/server/src/com/cloud/deploy/FirstFitPlanner.java b/server/src/com/cloud/deploy/FirstFitPlanner.java index e8504a991c1..caf8c6e92db 100755 --- a/server/src/com/cloud/deploy/FirstFitPlanner.java +++ b/server/src/com/cloud/deploy/FirstFitPlanner.java @@ -49,6 +49,7 @@ import com.cloud.dc.Pod; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.HostPodDao; +import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.exception.InsufficientServerCapacityException; import com.cloud.host.Host; import com.cloud.host.HostVO; @@ -81,7 +82,7 @@ import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; @Local(value=DeploymentPlanner.class) -public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { +public class FirstFitPlanner extends PlannerBase implements DeploymentClusterPlanner { private static final Logger s_logger = Logger.getLogger(FirstFitPlanner.class); @Inject protected HostDao _hostDao; @Inject protected DataCenterDao _dcDao; @@ -103,28 +104,13 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { @Inject DataStoreManager dataStoreMgr; @Inject protected ClusterDetailsDao _clusterDetailsDao; - protected List _storagePoolAllocators; - public List getStoragePoolAllocators() { - return _storagePoolAllocators; - } - public void setStoragePoolAllocators( - List _storagePoolAllocators) { - this._storagePoolAllocators = _storagePoolAllocators; - } - - protected List _hostAllocators; - public List getHostAllocators() { - return _hostAllocators; - } - public void setHostAllocators(List _hostAllocators) { - this._hostAllocators = _hostAllocators; - } protected String _allocationAlgorithm = "random"; + protected String _globalDeploymentPlanner = "FirstFitPlanner"; @Override - public DeployDestination plan(VirtualMachineProfile vmProfile, + public List orderClusters(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid) throws InsufficientServerCapacityException { VirtualMachine vm = vmProfile.getVirtualMachine(); @@ -138,136 +124,19 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { return null; } - ServiceOffering offering = vmProfile.getServiceOffering(); - int cpu_requested = offering.getCpu() * offering.getSpeed(); - long ram_requested = offering.getRamSize() * 1024L * 1024L; - - - if (s_logger.isDebugEnabled()) { - s_logger.debug("DeploymentPlanner allocation algorithm: "+_allocationAlgorithm); - - s_logger.debug("Trying to allocate a host and storage pools from dc:" + plan.getDataCenterId() + ", pod:" + plan.getPodId() + ",cluster:" + plan.getClusterId() + - ", requested cpu: " + cpu_requested + ", requested ram: " + ram_requested); - - s_logger.debug("Is ROOT volume READY (pool already allocated)?: " + (plan.getPoolId()!=null ? "Yes": "No")); - } - - String haVmTag = (String)vmProfile.getParameter(VirtualMachineProfile.Param.HaTag); - - if(plan.getHostId() != null && haVmTag == null){ - Long hostIdSpecified = plan.getHostId(); - if (s_logger.isDebugEnabled()){ - s_logger.debug("DeploymentPlan has host_id specified, choosing this host and making no checks on this host: " - + hostIdSpecified); - } - HostVO host = _hostDao.findById(hostIdSpecified); - if (host == null) { - s_logger.debug("The specified host cannot be found"); - } else if (avoid.shouldAvoid(host)) { - s_logger.debug("The specified host is in avoid set"); - } else { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Looking for suitable pools for this host under zone: "+host.getDataCenterId() +", pod: "+ host.getPodId()+", cluster: "+ host.getClusterId()); - } - - // search for storage under the zone, pod, cluster of the host. - DataCenterDeployment lastPlan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), - host.getClusterId(), hostIdSpecified, plan.getPoolId(), null, plan.getReservationContext()); - - Pair>, List> result = findSuitablePoolsForVolumes(vmProfile, - lastPlan, avoid, HostAllocator.RETURN_UPTO_ALL); - Map> suitableVolumeStoragePools = result.first(); - List readyAndReusedVolumes = result.second(); - - // choose the potential pool for this VM for this host - if (!suitableVolumeStoragePools.isEmpty()) { - List suitableHosts = new ArrayList(); - suitableHosts.add(host); - - Pair> potentialResources = findPotentialDeploymentResources( - suitableHosts, suitableVolumeStoragePools); - if (potentialResources != null) { - Pod pod = _podDao.findById(host.getPodId()); - Cluster cluster = _clusterDao.findById(host.getClusterId()); - Map storageVolMap = potentialResources.second(); - // remove the reused vol<->pool from destination, since - // we don't have to prepare this volume. - for (Volume vol : readyAndReusedVolumes) { - storageVolMap.remove(vol); - } - DeployDestination dest = new DeployDestination(dc, pod, cluster, host, storageVolMap); - s_logger.debug("Returning Deployment Destination: " + dest); - return dest; - } - } - } - s_logger.debug("Cannnot deploy to specified host, returning."); - return null; - } - - if (vm.getLastHostId() != null && haVmTag == null) { - s_logger.debug("This VM has last host_id specified, trying to choose the same host: " +vm.getLastHostId()); - - HostVO host = _hostDao.findById(vm.getLastHostId()); - if(host == null){ - s_logger.debug("The last host of this VM cannot be found"); - }else if(avoid.shouldAvoid(host)){ - s_logger.debug("The last host of this VM is in avoid set"); - }else if(_capacityMgr.checkIfHostReachMaxGuestLimit(host)){ - s_logger.debug("The last Host, hostId: "+ host.getId() +" already has max Running VMs(count includes system VMs), skipping this and trying other available hosts"); - }else{ - if (host.getStatus() == Status.Up && host.getResourceState() == ResourceState.Enabled) { - long cluster_id = host.getClusterId(); - ClusterDetailsVO cluster_detail_cpu = _clusterDetailsDao.findDetail(cluster_id,"cpuOvercommitRatio"); - ClusterDetailsVO cluster_detail_ram = _clusterDetailsDao.findDetail(cluster_id,"memoryOvercommitRatio"); - Float cpuOvercommitRatio = Float.parseFloat(cluster_detail_cpu.getValue()); - Float memoryOvercommitRatio = Float.parseFloat(cluster_detail_ram.getValue()); - if(_capacityMgr.checkIfHostHasCapacity(host.getId(), cpu_requested, ram_requested, true, cpuOvercommitRatio, memoryOvercommitRatio, true)){ - s_logger.debug("The last host of this VM is UP and has enough capacity"); - s_logger.debug("Now checking for suitable pools under zone: "+host.getDataCenterId() +", pod: "+ host.getPodId()+", cluster: "+ host.getClusterId()); - //search for storage under the zone, pod, cluster of the last host. - DataCenterDeployment lastPlan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), host.getClusterId(), host.getId(), plan.getPoolId(), null); - Pair>, List> result = findSuitablePoolsForVolumes(vmProfile, lastPlan, avoid, HostAllocator.RETURN_UPTO_ALL); - Map> suitableVolumeStoragePools = result.first(); - List readyAndReusedVolumes = result.second(); - //choose the potential pool for this VM for this host - if(!suitableVolumeStoragePools.isEmpty()){ - List suitableHosts = new ArrayList(); - suitableHosts.add(host); - - Pair> potentialResources = findPotentialDeploymentResources(suitableHosts, suitableVolumeStoragePools); - if(potentialResources != null){ - Pod pod = _podDao.findById(host.getPodId()); - Cluster cluster = _clusterDao.findById(host.getClusterId()); - Map storageVolMap = potentialResources.second(); - // remove the reused vol<->pool from destination, since we don't have to prepare this volume. - for(Volume vol : readyAndReusedVolumes){ - storageVolMap.remove(vol); - } - DeployDestination dest = new DeployDestination(dc, pod, cluster, host, storageVolMap); - s_logger.debug("Returning Deployment Destination: "+ dest); - return dest; - } - } - }else{ - s_logger.debug("The last host of this VM does not have enough capacity"); - } - }else{ - s_logger.debug("The last host of this VM is not UP or is not enabled, host status is: "+host.getStatus().name() + ", host resource state is: "+host.getResourceState()); - } - } - s_logger.debug("Cannot choose the last host to deploy this VM "); - } - - List clusterList = new ArrayList(); if (plan.getClusterId() != null) { Long clusterIdSpecified = plan.getClusterId(); s_logger.debug("Searching resources only under specified Cluster: "+ clusterIdSpecified); ClusterVO cluster = _clusterDao.findById(plan.getClusterId()); if (cluster != null ){ - clusterList.add(clusterIdSpecified); - return checkClustersforDestination(clusterList, vmProfile, plan, avoid, dc); + if (avoid.shouldAvoid(cluster)) { + s_logger.debug("The specified cluster is in avoid set, returning."); + } else { + clusterList.add(clusterIdSpecified); + removeClustersCrossingThreshold(clusterList, avoid, vmProfile, plan); + } + return clusterList; }else{ s_logger.debug("The specified cluster cannot be found, returning."); avoid.addCluster(plan.getClusterId()); @@ -280,11 +149,15 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { HostPodVO pod = _podDao.findById(podIdSpecified); if (pod != null) { - DeployDestination dest = scanClustersForDestinationInZoneOrPod(podIdSpecified, false, vmProfile, plan, avoid); - if(dest == null){ - avoid.addPod(plan.getPodId()); + if (avoid.shouldAvoid(pod)) { + s_logger.debug("The specified pod is in avoid set, returning."); + } else { + clusterList = scanClustersForDestinationInZoneOrPod(podIdSpecified, false, vmProfile, plan, avoid); + if (clusterList == null) { + avoid.addPod(plan.getPodId()); + } } - return dest; + return clusterList; } else { s_logger.debug("The specified Pod cannot be found, returning."); avoid.addPod(plan.getPodId()); @@ -305,7 +178,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { } - private DeployDestination scanPodsForDestination(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid){ + private List scanPodsForDestination(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid){ ServiceOffering offering = vmProfile.getServiceOffering(); int requiredCpu = offering.getCpu() * offering.getSpeed(); @@ -341,20 +214,24 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { if(!podsWithCapacity.isEmpty()){ prioritizedPodIds = reorderPods(podCapacityInfo, vmProfile, plan); + if (prioritizedPodIds == null || prioritizedPodIds.isEmpty()) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("No Pods found for destination, returning."); + } + return null; + } + List clusterList = new ArrayList(); //loop over pods for(Long podId : prioritizedPodIds){ s_logger.debug("Checking resources under Pod: "+podId); - DeployDestination dest = scanClustersForDestinationInZoneOrPod(podId, false, vmProfile, plan, avoid); - if(dest != null){ - return dest; + List clustersUnderPod = scanClustersForDestinationInZoneOrPod(podId, false, vmProfile, plan, + avoid); + if (clustersUnderPod != null) { + clusterList.addAll(clustersUnderPod); } - avoid.addPod(podId); } - if (s_logger.isDebugEnabled()) { - s_logger.debug("No Pods found for destination, returning."); - } - return null; + return clusterList; }else{ if (s_logger.isDebugEnabled()) { s_logger.debug("No Pods found after removing disabled pods and pods in avoid list, returning."); @@ -363,7 +240,69 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { } } - private DeployDestination scanClustersForDestinationInZoneOrPod(long id, boolean isZone, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid){ + private Map getCapacityThresholdMap() { + // Lets build this real time so that the admin wont have to restart MS + // if he changes these values + Map disableThresholdMap = new HashMap(); + + String cpuDisableThresholdString = _configDao.getValue(Config.CPUCapacityDisableThreshold.key()); + float cpuDisableThreshold = NumbersUtil.parseFloat(cpuDisableThresholdString, 0.85F); + disableThresholdMap.put(Capacity.CAPACITY_TYPE_CPU, cpuDisableThreshold); + + String memoryDisableThresholdString = _configDao.getValue(Config.MemoryCapacityDisableThreshold.key()); + float memoryDisableThreshold = NumbersUtil.parseFloat(memoryDisableThresholdString, 0.85F); + disableThresholdMap.put(Capacity.CAPACITY_TYPE_MEMORY, memoryDisableThreshold); + + return disableThresholdMap; + } + + private List getCapacitiesForCheckingThreshold() { + List capacityList = new ArrayList(); + capacityList.add(Capacity.CAPACITY_TYPE_CPU); + capacityList.add(Capacity.CAPACITY_TYPE_MEMORY); + return capacityList; + } + + private void removeClustersCrossingThreshold(List clusterListForVmAllocation, ExcludeList avoid, + VirtualMachineProfile vmProfile, DeploymentPlan plan) { + + List capacityList = getCapacitiesForCheckingThreshold(); + List clustersCrossingThreshold = new ArrayList(); + + ServiceOffering offering = vmProfile.getServiceOffering(); + int cpu_requested = offering.getCpu() * offering.getSpeed(); + long ram_requested = offering.getRamSize() * 1024L * 1024L; + + // For each capacity get the cluster list crossing the threshold and + // remove it from the clusterList that will be used for vm allocation. + for (short capacity : capacityList) { + + if (clusterListForVmAllocation == null || clusterListForVmAllocation.size() == 0) { + return; + } + if (capacity == Capacity.CAPACITY_TYPE_CPU) { + clustersCrossingThreshold = _capacityDao.listClustersCrossingThreshold(capacity, + plan.getDataCenterId(), Config.CPUCapacityDisableThreshold.key(), cpu_requested); + } else if (capacity == Capacity.CAPACITY_TYPE_MEMORY) { + clustersCrossingThreshold = _capacityDao.listClustersCrossingThreshold(capacity, + plan.getDataCenterId(), Config.MemoryCapacityDisableThreshold.key(), ram_requested); + } + + if (clustersCrossingThreshold != null && clustersCrossingThreshold.size() != 0) { + // addToAvoid Set + avoid.addClusterList(clustersCrossingThreshold); + // Remove clusters crossing disabled threshold + clusterListForVmAllocation.removeAll(clustersCrossingThreshold); + + s_logger.debug("Cannot allocate cluster list " + clustersCrossingThreshold.toString() + " for vm creation since their allocated percentage" + + " crosses the disable capacity threshold defined at each cluster/ at global value for capacity Type : " + capacity + ", skipping these clusters"); + } + + } + } + + private List scanClustersForDestinationInZoneOrPod(long id, boolean isZone, + VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid) { VirtualMachine vm = vmProfile.getVirtualMachine(); ServiceOffering offering = vmProfile.getServiceOffering(); @@ -396,6 +335,9 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { prioritizedClusterIds.removeAll(disabledClusters); } } + + removeClustersCrossingThreshold(prioritizedClusterIds, avoid, vmProfile, plan); + }else{ if (s_logger.isDebugEnabled()) { s_logger.debug("No clusters found having a host with enough capacity, returning."); @@ -404,7 +346,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { } if(!prioritizedClusterIds.isEmpty()){ List clusterList = reorderClusters(id, isZone, clusterCapacityInfo, vmProfile, plan); - return checkClustersforDestination(clusterList, vmProfile, plan, avoid, dc); + return clusterList; //return checkClustersforDestination(clusterList, vmProfile, plan, avoid, dc); }else{ if (s_logger.isDebugEnabled()) { s_logger.debug("No clusters found after removing disabled clusters and clusters in avoid list, returning."); @@ -452,114 +394,6 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { return disabledPods; } - private List getCapacitiesForCheckingThreshold(){ - List capacityList = new ArrayList(); - capacityList.add(Capacity.CAPACITY_TYPE_CPU); - capacityList.add(Capacity.CAPACITY_TYPE_MEMORY); - return capacityList; - } - - private void removeClustersCrossingThreshold(List clusterListForVmAllocation, ExcludeList avoid, VirtualMachineProfile vmProfile, DeploymentPlan plan){ - - List capacityList = getCapacitiesForCheckingThreshold(); - List clustersCrossingThreshold = new ArrayList(); - - ServiceOffering offering = vmProfile.getServiceOffering(); - int cpu_requested = offering.getCpu() * offering.getSpeed(); - long ram_requested = offering.getRamSize() * 1024L * 1024L; - - // For each capacity get the cluster list crossing the threshold and remove it from the clusterList that will be used for vm allocation. - for(short capacity : capacityList){ - - if (clusterListForVmAllocation == null || clusterListForVmAllocation.size() == 0){ - return; - } - if (capacity == Capacity.CAPACITY_TYPE_CPU) { - clustersCrossingThreshold = _capacityDao.listClustersCrossingThreshold(capacity, plan.getDataCenterId(), Config.CPUCapacityDisableThreshold.key(), cpu_requested); - } - else if (capacity == Capacity.CAPACITY_TYPE_MEMORY ) { - clustersCrossingThreshold = _capacityDao.listClustersCrossingThreshold(capacity, plan.getDataCenterId(), - Config.MemoryCapacityDisableThreshold.key(), ram_requested ); - } - - - if (clustersCrossingThreshold != null && clustersCrossingThreshold.size() != 0){ - // addToAvoid Set - avoid.addClusterList(clustersCrossingThreshold); - // Remove clusters crossing disabled threshold - clusterListForVmAllocation.removeAll(clustersCrossingThreshold); - - s_logger.debug("Cannot allocate cluster list " + clustersCrossingThreshold.toString() + " for vm creation since their allocated percentage" + - " crosses the disable capacity threshold defined at each cluster/ at global value for capacity Type : " + capacity + ", skipping these clusters"); - } - - } - } - - private DeployDestination checkClustersforDestination(List clusterList, VirtualMachineProfile vmProfile, - DeploymentPlan plan, ExcludeList avoid, DataCenter dc){ - - if (s_logger.isTraceEnabled()) { - s_logger.trace("ClusterId List to consider: " + clusterList); - } - - removeClustersCrossingThreshold(clusterList, avoid, vmProfile, plan); - - for(Long clusterId : clusterList){ - Cluster clusterVO = _clusterDao.findById(clusterId); - - if (clusterVO.getHypervisorType() != vmProfile.getHypervisorType()) { - s_logger.debug("Cluster: "+clusterId + " has HyperVisorType that does not match the VM, skipping this cluster"); - avoid.addCluster(clusterVO.getId()); - continue; - } - - s_logger.debug("Checking resources in Cluster: "+clusterId + " under Pod: "+clusterVO.getPodId()); - //search for resources(hosts and storage) under this zone, pod, cluster. - DataCenterDeployment potentialPlan = new DataCenterDeployment(plan.getDataCenterId(), clusterVO.getPodId(), clusterVO.getId(), null, plan.getPoolId(), null, plan.getReservationContext()); - - //find suitable hosts under this cluster, need as many hosts as we get. - List suitableHosts = findSuitableHosts(vmProfile, potentialPlan, avoid, HostAllocator.RETURN_UPTO_ALL); - //if found suitable hosts in this cluster, find suitable storage pools for each volume of the VM - if(suitableHosts != null && !suitableHosts.isEmpty()){ - if (vmProfile.getHypervisorType() == HypervisorType.BareMetal) { - Pod pod = _podDao.findById(clusterVO.getPodId()); - DeployDestination dest = new DeployDestination(dc, pod, clusterVO, suitableHosts.get(0)); - return dest; - } - - Pair>, List> result = findSuitablePoolsForVolumes(vmProfile, potentialPlan, avoid, StoragePoolAllocator.RETURN_UPTO_ALL); - Map> suitableVolumeStoragePools = result.first(); - List readyAndReusedVolumes = result.second(); - - //choose the potential host and pool for the VM - if(!suitableVolumeStoragePools.isEmpty()){ - Pair> potentialResources = findPotentialDeploymentResources(suitableHosts, suitableVolumeStoragePools); - - if(potentialResources != null){ - Pod pod = _podDao.findById(clusterVO.getPodId()); - Host host = _hostDao.findById(potentialResources.first().getId()); - Map storageVolMap = potentialResources.second(); - // remove the reused vol<->pool from destination, since we don't have to prepare this volume. - for(Volume vol : readyAndReusedVolumes){ - storageVolMap.remove(vol); - } - DeployDestination dest = new DeployDestination(dc, pod, clusterVO, host, storageVolMap ); - s_logger.debug("Returning Deployment Destination: "+ dest); - return dest; - } - }else{ - s_logger.debug("No suitable storagePools found under this Cluster: "+clusterId); - } - }else{ - s_logger.debug("No suitable hosts found under this Cluster: "+clusterId); - } - avoid.addCluster(clusterVO.getId()); - } - s_logger.debug("Could not find suitable Deployment Destination for this VM under any clusters, returning. "); - return null; - } - protected Pair, Map> listClustersByCapacity(long id, int requiredCpu, long requiredRam, ExcludeList avoid, boolean isZone){ //look at the aggregate available cpu and ram per cluster @@ -630,215 +464,6 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { } - - protected Pair> findPotentialDeploymentResources(List suitableHosts, Map> suitableVolumeStoragePools){ - s_logger.debug("Trying to find a potenial host and associated storage pools from the suitable host/pool lists for this VM"); - - boolean hostCanAccessPool = false; - boolean haveEnoughSpace = false; - Map storage = new HashMap(); - TreeSet volumesOrderBySizeDesc = new TreeSet(new Comparator() { - @Override - public int compare(Volume v1, Volume v2) { - if(v1.getSize() < v2.getSize()) - return 1; - else - return -1; - } - }); - volumesOrderBySizeDesc.addAll(suitableVolumeStoragePools.keySet()); - boolean multipleVolume = volumesOrderBySizeDesc.size() > 1; - for(Host potentialHost : suitableHosts){ - Map> volumeAllocationMap = new HashMap>(); - for(Volume vol : volumesOrderBySizeDesc){ - haveEnoughSpace = false; - s_logger.debug("Checking if host: "+potentialHost.getId() +" can access any suitable storage pool for volume: "+ vol.getVolumeType()); - List volumePoolList = suitableVolumeStoragePools.get(vol); - hostCanAccessPool = false; - for(StoragePool potentialSPool : volumePoolList){ - if(hostCanAccessSPool(potentialHost, potentialSPool)){ - hostCanAccessPool = true; - if(multipleVolume){ - List requestVolumes = null; - if(volumeAllocationMap.containsKey(potentialSPool)) - requestVolumes = volumeAllocationMap.get(potentialSPool); - else - requestVolumes = new ArrayList(); - requestVolumes.add(vol); - - if(!_storageMgr.storagePoolHasEnoughSpace(requestVolumes, potentialSPool)) - continue; - volumeAllocationMap.put(potentialSPool,requestVolumes); - } - storage.put(vol, potentialSPool); - haveEnoughSpace = true; - break; - } - } - if(!hostCanAccessPool){ - break; - } - if(!haveEnoughSpace) { - s_logger.warn("insufficient capacity to allocate all volumes"); - break; - } - } - if(hostCanAccessPool && haveEnoughSpace){ - s_logger.debug("Found a potential host " + "id: "+potentialHost.getId() + " name: " +potentialHost.getName() + " and associated storage pools for this VM"); - return new Pair>(potentialHost, storage); - } - } - s_logger.debug("Could not find a potential host that has associated storage pools from the suitable host/pool lists for this VM"); - return null; - } - - protected boolean hostCanAccessSPool(Host host, StoragePool pool){ - boolean hostCanAccessSPool = false; - - StoragePoolHostVO hostPoolLinkage = _poolHostDao.findByPoolHost(pool.getId(), host.getId()); - if(hostPoolLinkage != null){ - hostCanAccessSPool = true; - } - - s_logger.debug("Host: "+ host.getId() + (hostCanAccessSPool ?" can" : " cannot") + " access pool: "+ pool.getId()); - return hostCanAccessSPool; - } - - protected List findSuitableHosts(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo){ - List suitableHosts = new ArrayList(); - for(HostAllocator allocator : _hostAllocators) { - suitableHosts = allocator.allocateTo(vmProfile, plan, Host.Type.Routing, avoid, returnUpTo); - if (suitableHosts != null && !suitableHosts.isEmpty()) { - break; - } - } - - if(suitableHosts.isEmpty()){ - s_logger.debug("No suitable hosts found"); - } - return suitableHosts; - } - - protected Pair>, List> findSuitablePoolsForVolumes(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo){ - List volumesTobeCreated = _volsDao.findUsableVolumesForInstance(vmProfile.getId()); - Map> suitableVolumeStoragePools = new HashMap>(); - List readyAndReusedVolumes = new ArrayList(); - - //for each volume find list of suitable storage pools by calling the allocators - for (VolumeVO toBeCreated : volumesTobeCreated) { - s_logger.debug("Checking suitable pools for volume (Id, Type): ("+toBeCreated.getId() +"," +toBeCreated.getVolumeType().name() + ")"); - - //If the plan specifies a poolId, it means that this VM's ROOT volume is ready and the pool should be reused. - //In this case, also check if rest of the volumes are ready and can be reused. - if(plan.getPoolId() != null){ - s_logger.debug("Volume has pool(" + plan.getPoolId() + ") already allocated, checking if pool can be reused, poolId: "+toBeCreated.getPoolId()); - List suitablePools = new ArrayList(); - StoragePool pool = null; - if(toBeCreated.getPoolId() != null){ - s_logger.debug("finding pool by id '" + toBeCreated.getPoolId() + "'"); - pool = (StoragePool)this.dataStoreMgr.getPrimaryDataStore(toBeCreated.getPoolId()); - }else{ - s_logger.debug("finding pool by id '" + plan.getPoolId() + "'"); - pool = (StoragePool)this.dataStoreMgr.getPrimaryDataStore(plan.getPoolId()); - } - - if(pool != null){ - if(!pool.isInMaintenance()){ - if(!avoid.shouldAvoid(pool)){ - long exstPoolDcId = pool.getDataCenterId(); - - long exstPoolPodId = pool.getPodId() != null ? pool.getPodId() : -1; - long exstPoolClusterId = pool.getClusterId() != null ? pool.getClusterId() : -1; - if(plan.getDataCenterId() == exstPoolDcId && plan.getPodId() == exstPoolPodId && plan.getClusterId() == exstPoolClusterId){ - s_logger.debug("Planner need not allocate a pool for this volume since its READY"); - suitablePools.add(pool); - suitableVolumeStoragePools.put(toBeCreated, suitablePools); - if (!(toBeCreated.getState() == Volume.State.Allocated || toBeCreated.getState() == Volume.State.Creating)) { - readyAndReusedVolumes.add(toBeCreated); - } - continue; - }else{ - s_logger.debug("Pool of the volume does not fit the specified plan, need to reallocate a pool for this volume"); - } - }else{ - s_logger.debug("Pool of the volume is in avoid set, need to reallocate a pool for this volume"); - } - }else{ - s_logger.debug("Pool of the volume is in maintenance, need to reallocate a pool for this volume"); - } - }else{ - s_logger.debug("Unable to find pool by provided id"); - } - } - - if(s_logger.isDebugEnabled()){ - s_logger.debug("We need to allocate new storagepool for this volume"); - } - if(!isRootAdmin(plan.getReservationContext())){ - if(!isEnabledForAllocation(plan.getDataCenterId(), plan.getPodId(), plan.getClusterId())){ - if(s_logger.isDebugEnabled()){ - s_logger.debug("Cannot allocate new storagepool for this volume in this cluster, allocation state is disabled"); - s_logger.debug("Cannot deploy to this specified plan, allocation state is disabled, returning."); - } - //Cannot find suitable storage pools under this cluster for this volume since allocation_state is disabled. - //- remove any suitable pools found for other volumes. - //All volumes should get suitable pools under this cluster; else we cant use this cluster. - suitableVolumeStoragePools.clear(); - break; - } - } - - s_logger.debug("Calling StoragePoolAllocators to find suitable pools"); - - DiskOfferingVO diskOffering = _diskOfferingDao.findById(toBeCreated.getDiskOfferingId()); - DiskProfile diskProfile = new DiskProfile(toBeCreated, diskOffering, vmProfile.getHypervisorType()); - - boolean useLocalStorage = false; - if (vmProfile.getType() != VirtualMachine.Type.User) { - String ssvmUseLocalStorage = _configDao.getValue(Config.SystemVMUseLocalStorage.key()); - if (ssvmUseLocalStorage.equalsIgnoreCase("true")) { - useLocalStorage = true; - } - } else { - useLocalStorage = diskOffering.getUseLocalStorage(); - - // TODO: this is a hacking fix for the problem of deploy ISO-based VM on local storage - // when deploying VM based on ISO, we have a service offering and an additional disk offering, use-local storage flag is actually - // saved in service offering, overrde the flag from service offering when it is a ROOT disk - if(!useLocalStorage && vmProfile.getServiceOffering().getUseLocalStorage()) { - if(toBeCreated.getVolumeType() == Volume.Type.ROOT) - useLocalStorage = true; - } - } - diskProfile.setUseLocalStorage(useLocalStorage); - - boolean foundPotentialPools = false; - for(StoragePoolAllocator allocator : _storagePoolAllocators) { - final List suitablePools = allocator.allocateToPool(diskProfile, vmProfile, plan, avoid, returnUpTo); - if (suitablePools != null && !suitablePools.isEmpty()) { - suitableVolumeStoragePools.put(toBeCreated, suitablePools); - foundPotentialPools = true; - break; - } - } - - if(!foundPotentialPools){ - s_logger.debug("No suitable pools found for volume: "+toBeCreated +" under cluster: "+plan.getClusterId()); - //No suitable storage pools found under this cluster for this volume. - remove any suitable pools found for other volumes. - //All volumes should get suitable pools under this cluster; else we cant use this cluster. - suitableVolumeStoragePools.clear(); - break; - } - } - - if(suitableVolumeStoragePools.isEmpty()){ - s_logger.debug("No suitable pools found"); - } - - return new Pair>, List>(suitableVolumeStoragePools, readyAndReusedVolumes); - } - - private boolean isRootAdmin(ReservationContext reservationContext) { if(reservationContext != null){ if(reservationContext.getAccount() != null){ @@ -859,10 +484,17 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { @Override public boolean canHandle(VirtualMachineProfile vm, DeploymentPlan plan, ExcludeList avoid) { - if(vm.getHypervisorType() != HypervisorType.BareMetal){ - //check the allocation strategy - if (_allocationAlgorithm != null && (_allocationAlgorithm.equals(AllocationAlgorithm.random.toString()) || _allocationAlgorithm.equals(AllocationAlgorithm.firstfit.toString()))) { - return true; + // check what the ServiceOffering says. If null, check the global config + ServiceOffering offering = vm.getServiceOffering(); + if (vm.getHypervisorType() != HypervisorType.BareMetal) { + if (offering != null && offering.getDeploymentPlanner() != null) { + if (offering.getDeploymentPlanner().equals(this.getName())) { + return true; + } + } else { + if (_globalDeploymentPlanner != null && _globalDeploymentPlanner.equals(this._name)) { + return true; + } } } return false; @@ -872,29 +504,20 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { public boolean configure(String name, Map params) throws ConfigurationException { super.configure(name, params); _allocationAlgorithm = _configDao.getValue(Config.VmAllocationAlgorithm.key()); + _globalDeploymentPlanner = _configDao.getValue(Config.VmDeploymentPlanner.key()); return true; } - private boolean isEnabledForAllocation(long zoneId, Long podId, Long clusterId){ - // Check if the zone exists in the system - DataCenterVO zone = _dcDao.findById(zoneId); - if(zone != null && Grouping.AllocationState.Disabled == zone.getAllocationState()){ - s_logger.info("Zone is currently disabled, cannot allocate to this zone: "+ zoneId); - return false; - } - Pod pod = _podDao.findById(podId); - if(pod != null && Grouping.AllocationState.Disabled == pod.getAllocationState()){ - s_logger.info("Pod is currently disabled, cannot allocate to this pod: "+ podId); - return false; - } + @Override + public DeployDestination plan(VirtualMachineProfile vm, DeploymentPlan plan, + ExcludeList avoid) throws InsufficientServerCapacityException { + // TODO Auto-generated method stub + return null; + } - Cluster cluster = _clusterDao.findById(clusterId); - if(cluster != null && Grouping.AllocationState.Disabled == cluster.getAllocationState()){ - s_logger.info("Cluster is currently disabled, cannot allocate to this cluster: "+ clusterId); - return false; - } - - return true; + @Override + public PlannerResourceUsage getResourceUsage() { + return PlannerResourceUsage.Shared; } } diff --git a/server/src/com/cloud/deploy/HypervisorVmPlannerSelector.java b/server/src/com/cloud/deploy/HypervisorVmPlannerSelector.java deleted file mode 100755 index ce494051376..00000000000 --- a/server/src/com/cloud/deploy/HypervisorVmPlannerSelector.java +++ /dev/null @@ -1,54 +0,0 @@ -// 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 javax.ejb.Local; - -import org.apache.log4j.Logger; - -import com.cloud.deploy.DeploymentPlanner.AllocationAlgorithm; -import com.cloud.hypervisor.Hypervisor.HypervisorType; -import com.cloud.vm.UserVmVO; - -@Local(value = {DeployPlannerSelector.class}) -public class HypervisorVmPlannerSelector extends AbstractDeployPlannerSelector { - private static final Logger s_logger = Logger.getLogger(HypervisorVmPlannerSelector.class); - - @Override - public String selectPlanner(UserVmVO vm) { - if (vm.getHypervisorType() != HypervisorType.BareMetal) { - //check the allocation strategy - if (_allocationAlgorithm != null) { - if (_allocationAlgorithm.equals(AllocationAlgorithm.random.toString()) - || _allocationAlgorithm.equals(AllocationAlgorithm.firstfit.toString())) { - return "FirstFitPlanner"; - } else if (_allocationAlgorithm.equals(AllocationAlgorithm.userdispersing.toString())) { - return "UserDispersingPlanner"; - } else if (_allocationAlgorithm.equals(AllocationAlgorithm.userconcentratedpod_random.toString()) - || _allocationAlgorithm.equals(AllocationAlgorithm.userconcentratedpod_firstfit.toString())) { - return "UserConcentratedPodPlanner"; - } - } else { - if (s_logger.isDebugEnabled()) { - s_logger.debug("The allocation algorithm is null, cannot select the planner"); - } - } - } - - return null; - } -} diff --git a/server/src/com/cloud/deploy/PlannerHostReservationVO.java b/server/src/com/cloud/deploy/PlannerHostReservationVO.java new file mode 100644 index 00000000000..cf5f03177f7 --- /dev/null +++ b/server/src/com/cloud/deploy/PlannerHostReservationVO.java @@ -0,0 +1,117 @@ +// 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 javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import org.apache.cloudstack.api.InternalIdentity; + +import com.cloud.deploy.DeploymentPlanner.PlannerResourceUsage; + +@Entity +@Table(name = "op_host_planner_reservation") +public class PlannerHostReservationVO implements InternalIdentity { + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="id") + private long id; + + @Column(name="host_id") + private Long hostId; + + @Column(name="data_center_id") + private Long dataCenterId; + + @Column(name="pod_id") + private Long podId; + + @Column(name="cluster_id") + private Long clusterId; + + @Column(name = "resource_usage") + @Enumerated(EnumType.STRING) + private PlannerResourceUsage resourceUsage; + + public PlannerHostReservationVO() { + } + + public PlannerHostReservationVO(Long hostId, Long dataCenterId, Long podId, Long clusterId) { + this.hostId = hostId; + this.dataCenterId = dataCenterId; + this.podId = podId; + this.clusterId = clusterId; + } + + public PlannerHostReservationVO(Long hostId, Long dataCenterId, Long podId, Long clusterId, + PlannerResourceUsage resourceUsage) { + this.hostId = hostId; + this.dataCenterId = dataCenterId; + this.podId = podId; + this.clusterId = clusterId; + this.resourceUsage = resourceUsage; + } + + @Override + public long getId() { + return id; + } + + public Long getHostId() { + return hostId; + } + + public void setHostId(Long hostId) { + this.hostId = hostId; + } + + public Long getDataCenterId() { + return dataCenterId; + } + public void setDataCenterId(Long dataCenterId) { + this.dataCenterId = dataCenterId; + } + + public Long getPodId() { + return podId; + } + public void setPodId(long podId) { + this.podId = new Long(podId); + } + + public Long getClusterId() { + return clusterId; + } + public void setClusterId(long clusterId) { + this.clusterId = new Long(clusterId); + } + + public PlannerResourceUsage getResourceUsage() { + return resourceUsage; + } + + public void setResourceUsage(PlannerResourceUsage resourceType) { + this.resourceUsage = resourceType; + } + +} diff --git a/server/src/com/cloud/deploy/DeployPlannerSelector.java b/server/src/com/cloud/deploy/dao/PlannerHostReservationDao.java old mode 100755 new mode 100644 similarity index 67% rename from server/src/com/cloud/deploy/DeployPlannerSelector.java rename to server/src/com/cloud/deploy/dao/PlannerHostReservationDao.java index 062b492d8fc..69118f13896 --- a/server/src/com/cloud/deploy/DeployPlannerSelector.java +++ b/server/src/com/cloud/deploy/dao/PlannerHostReservationDao.java @@ -1,24 +1,30 @@ -// 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 com.cloud.utils.component.Adapter; -import com.cloud.vm.UserVmVO; - -public interface DeployPlannerSelector extends Adapter { - String selectPlanner(UserVmVO vm); -} +// 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.dao; + +import java.util.List; + +import com.cloud.deploy.PlannerHostReservationVO; +import com.cloud.utils.db.GenericDao; + +public interface PlannerHostReservationDao extends GenericDao { + + PlannerHostReservationVO findByHostId(long hostId); + + List listAllReservedHosts(); + +} diff --git a/server/src/com/cloud/deploy/dao/PlannerHostReservationDaoImpl.java b/server/src/com/cloud/deploy/dao/PlannerHostReservationDaoImpl.java new file mode 100644 index 00000000000..41e09647d7e --- /dev/null +++ b/server/src/com/cloud/deploy/dao/PlannerHostReservationDaoImpl.java @@ -0,0 +1,63 @@ +// 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.dao; + +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.ejb.Local; +import com.cloud.deploy.PlannerHostReservationVO; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@Local(value = { PlannerHostReservationDao.class }) +public class PlannerHostReservationDaoImpl extends GenericDaoBase implements + PlannerHostReservationDao { + + private SearchBuilder _hostIdSearch; + private SearchBuilder _reservedHostSearch; + + public PlannerHostReservationDaoImpl() { + + } + + @PostConstruct + protected void init() { + _hostIdSearch = createSearchBuilder(); + _hostIdSearch.and("hostId", _hostIdSearch.entity().getHostId(), SearchCriteria.Op.EQ); + _hostIdSearch.done(); + + _reservedHostSearch = createSearchBuilder(); + _reservedHostSearch.and("usage", _reservedHostSearch.entity().getResourceUsage(), SearchCriteria.Op.NNULL); + _reservedHostSearch.done(); + } + + @Override + public PlannerHostReservationVO findByHostId(long hostId) { + SearchCriteria sc = _hostIdSearch.create(); + sc.setParameters("hostId", hostId); + return findOneBy(sc); + } + + @Override + public List listAllReservedHosts() { + SearchCriteria sc = _reservedHostSearch.create(); + return listBy(sc); + } + +} diff --git a/server/src/com/cloud/resource/ResourceManagerImpl.java b/server/src/com/cloud/resource/ResourceManagerImpl.java index 0ab35dd00a2..c60f0953a78 100755 --- a/server/src/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/com/cloud/resource/ResourceManagerImpl.java @@ -85,6 +85,10 @@ import com.cloud.dc.dao.ClusterVSMMapDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.DataCenterIpAddressDao; import com.cloud.dc.dao.HostPodDao; +import com.cloud.deploy.PlannerHostReservationVO; +import com.cloud.deploy.dao.PlannerHostReservationDao; +import com.cloud.event.ActionEvent; +import com.cloud.event.EventTypes; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.DiscoveryException; import com.cloud.exception.InvalidParameterValueException; @@ -212,6 +216,8 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, protected HighAvailabilityManager _haMgr; @Inject protected StorageService _storageSvr; + @Inject + PlannerHostReservationDao _plannerHostReserveDao; protected List _discoverers; public List getDiscoverers() { @@ -2851,4 +2857,41 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, ResourceState.Enabled); return sc.list(); } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_HOST_RESERVATION_RELEASE, eventDescription = "releasing host reservation", async = true) + public boolean releaseHostReservation(Long hostId) { + Transaction txn = Transaction.currentTxn(); + try { + txn.start(); + PlannerHostReservationVO reservationEntry = _plannerHostReserveDao.findByHostId(hostId); + if (reservationEntry != null) { + long id = reservationEntry.getId(); + PlannerHostReservationVO hostReservation = _plannerHostReserveDao.lockRow(id, true); + if (hostReservation == null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Host reservation for host: " + hostId + " does not even exist. Release reservartion call is ignored."); + } + txn.rollback(); + return false; + } + hostReservation.setResourceUsage(null); + _plannerHostReserveDao.persist(hostReservation); + txn.commit(); + return true; + } + if (s_logger.isDebugEnabled()) { + s_logger.debug("Host reservation for host: " + hostId + + " does not even exist. Release reservartion call is ignored."); + } + return false; + } catch (CloudRuntimeException e) { + throw e; + } catch (Throwable t) { + s_logger.error("Unable to release host reservation for host: " + hostId, t); + txn.rollback(); + return false; + } + } } diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index bc3728203bc..f74b7ad964c 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -78,6 +78,7 @@ import org.apache.cloudstack.api.command.admin.host.FindHostsForMigrationCmd; import org.apache.cloudstack.api.command.admin.host.ListHostsCmd; import org.apache.cloudstack.api.command.admin.host.PrepareForMaintenanceCmd; import org.apache.cloudstack.api.command.admin.host.ReconnectHostCmd; +import org.apache.cloudstack.api.command.admin.host.ReleaseHostReservationCmd; import org.apache.cloudstack.api.command.admin.host.UpdateHostCmd; import org.apache.cloudstack.api.command.admin.host.UpdateHostPasswordCmd; import org.apache.cloudstack.api.command.admin.internallb.ConfigureInternalLoadBalancerElementCmd; @@ -462,6 +463,7 @@ import com.cloud.dc.dao.HostPodDao; import com.cloud.dc.dao.PodVlanMapDao; import com.cloud.dc.dao.VlanDao; import com.cloud.deploy.DataCenterDeployment; +import com.cloud.deploy.DeploymentPlanner; import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; @@ -589,6 +591,7 @@ import com.cloud.vm.dao.VMInstanceDao; import edu.emory.mathcs.backport.java.util.Arrays; import edu.emory.mathcs.backport.java.util.Collections; +import org.apache.cloudstack.api.command.admin.config.ListDeploymentPlannersCmd; public class ManagementServerImpl extends ManagerBase implements ManagementServer { @@ -726,11 +729,21 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe private List _userAuthenticators; private List _userPasswordEncoders; + protected List _planners; + + public List getPlanners() { + return _planners; + } + + public void setPlanners(List _planners) { + this._planners = _planners; + } + @Inject ClusterManager _clusterMgr; private String _hashKey = null; private String _encryptionKey = null; private String _encryptionIV = null; - + @Inject protected AffinityGroupVMMapDao _affinityGroupVMMapDao; @@ -976,29 +989,29 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe String zoneType = cmd.getZoneType(); String keyword = cmd.getKeyword(); zoneId = _accountMgr.checkAccessAndSpecifyAuthority(UserContext.current().getCaller(), zoneId); - - + + Filter searchFilter = new Filter(ClusterVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal()); - - SearchBuilder sb = _clusterDao.createSearchBuilder(); - sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); - sb.and("name", sb.entity().getName(), SearchCriteria.Op.LIKE); - sb.and("podId", sb.entity().getPodId(), SearchCriteria.Op.EQ); - sb.and("dataCenterId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + + SearchBuilder sb = _clusterDao.createSearchBuilder(); + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + sb.and("name", sb.entity().getName(), SearchCriteria.Op.LIKE); + sb.and("podId", sb.entity().getPodId(), SearchCriteria.Op.EQ); + sb.and("dataCenterId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ); sb.and("clusterType", sb.entity().getClusterType(), SearchCriteria.Op.EQ); sb.and("allocationState", sb.entity().getAllocationState(), SearchCriteria.Op.EQ); - + if(zoneType != null) { SearchBuilder zoneSb = _dcDao.createSearchBuilder(); - zoneSb.and("zoneNetworkType", zoneSb.entity().getNetworkType(), SearchCriteria.Op.EQ); + zoneSb.and("zoneNetworkType", zoneSb.entity().getNetworkType(), SearchCriteria.Op.EQ); sb.join("zoneSb", zoneSb, sb.entity().getDataCenterId(), zoneSb.entity().getId(), JoinBuilder.JoinType.INNER); } - - - SearchCriteria sc = sb.create(); + + + SearchCriteria sc = sb.create(); if (id != null) { - sc.setParameters("id", id); + sc.setParameters("id", id); } if (name != null) { @@ -1026,9 +1039,9 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } if(zoneType != null) { - sc.setJoinParameters("zoneSb", "zoneNetworkType", zoneType); + sc.setJoinParameters("zoneSb", "zoneNetworkType", zoneType); } - + if (keyword != null) { SearchCriteria ssc = _clusterDao.createSearchCriteria(); ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); @@ -1441,26 +1454,26 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe public Pair, Integer> searchForPods(ListPodsByCmd cmd) { String podName = cmd.getPodName(); Long id = cmd.getId(); - Long zoneId = cmd.getZoneId(); + Long zoneId = cmd.getZoneId(); Object keyword = cmd.getKeyword(); Object allocationState = cmd.getAllocationState(); String zoneType = cmd.getZoneType(); zoneId = _accountMgr.checkAccessAndSpecifyAuthority(UserContext.current().getCaller(), zoneId); - + Filter searchFilter = new Filter(HostPodVO.class, "dataCenterId", true, cmd.getStartIndex(), cmd.getPageSizeVal()); - SearchBuilder sb = _hostPodDao.createSearchBuilder(); + SearchBuilder sb = _hostPodDao.createSearchBuilder(); sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); - sb.and("name", sb.entity().getName(), SearchCriteria.Op.LIKE); - sb.and("dataCenterId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.and("name", sb.entity().getName(), SearchCriteria.Op.LIKE); + sb.and("dataCenterId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); sb.and("allocationState", sb.entity().getAllocationState(), SearchCriteria.Op.EQ); - + if(zoneType != null) { SearchBuilder zoneSb = _dcDao.createSearchBuilder(); - zoneSb.and("zoneNetworkType", zoneSb.entity().getNetworkType(), SearchCriteria.Op.EQ); + zoneSb.and("zoneNetworkType", zoneSb.entity().getNetworkType(), SearchCriteria.Op.EQ); sb.join("zoneSb", zoneSb, sb.entity().getDataCenterId(), zoneSb.entity().getId(), JoinBuilder.JoinType.INNER); } - + SearchCriteria sc = sb.create(); if (keyword != null) { SearchCriteria ssc = _hostPodDao.createSearchCriteria(); @@ -1473,23 +1486,23 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe if (id != null) { sc.setParameters("id", id); } - + if (podName != null) { sc.setParameters("name", "%" + podName + "%"); } - + if (zoneId != null) { sc.setParameters("dataCenterId", zoneId); } - + if (allocationState != null) { sc.setParameters("allocationState", allocationState); - } - - if(zoneType != null) { - sc.setJoinParameters("zoneSb", "zoneNetworkType", zoneType); } - + + if(zoneType != null) { + sc.setJoinParameters("zoneSb", "zoneNetworkType", zoneType); + } + Pair, Integer> result = _hostPodDao.searchAndCount(sc, searchFilter); return new Pair, Integer>(result.first(), result.second()); } @@ -2903,7 +2916,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(ListAffinityGroupsCmd.class); cmdList.add(UpdateVMAffinityGroupCmd.class); cmdList.add(ListAffinityGroupTypesCmd.class); - + cmdList.add(ListDeploymentPlannersCmd.class); + cmdList.add(ReleaseHostReservationCmd.class); cmdList.add(AddResourceDetailCmd.class); cmdList.add(RemoveResourceDetailCmd.class); cmdList.add(ListResourceDetailsCmd.class); @@ -3105,10 +3119,10 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe if(zoneType != null) { SearchBuilder zoneSb = _dcDao.createSearchBuilder(); - zoneSb.and("zoneNetworkType", zoneSb.entity().getNetworkType(), SearchCriteria.Op.EQ); + zoneSb.and("zoneNetworkType", zoneSb.entity().getNetworkType(), SearchCriteria.Op.EQ); sb.join("zoneSb", zoneSb, sb.entity().getDataCenterId(), zoneSb.entity().getId(), JoinBuilder.JoinType.INNER); - } - + } + SearchCriteria sc = sb.create(); if (keyword != null) { @@ -3150,9 +3164,9 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } if(zoneType != null) { - sc.setJoinParameters("zoneSb", "zoneNetworkType", zoneType); + sc.setJoinParameters("zoneSb", "zoneNetworkType", zoneType); } - + Pair, Integer> result = _vmInstanceDao.searchAndCount(sc, searchFilter); return new Pair, Integer>(result.first(), result.second()); } @@ -3677,7 +3691,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe // although we may have race conditioning here, database transaction serialization should // give us the same key if (_hashKey == null) { - _hashKey = _configDao.getValueAndInitIfNotExist(Config.HashKey.key(), Config.HashKey.getCategory(), + _hashKey = _configDao.getValueAndInitIfNotExist(Config.HashKey.key(), Config.HashKey.getCategory(), getBase64EncodedRandomKey(128)); } return _hashKey; @@ -3686,41 +3700,41 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Override public String getEncryptionKey() { if (_encryptionKey == null) { - _encryptionKey = _configDao.getValueAndInitIfNotExist(Config.EncryptionKey.key(), - Config.EncryptionKey.getCategory(), + _encryptionKey = _configDao.getValueAndInitIfNotExist(Config.EncryptionKey.key(), + Config.EncryptionKey.getCategory(), getBase64EncodedRandomKey(128)); } return _encryptionKey; } - + @Override public String getEncryptionIV() { if (_encryptionIV == null) { - _encryptionIV = _configDao.getValueAndInitIfNotExist(Config.EncryptionIV.key(), - Config.EncryptionIV.getCategory(), + _encryptionIV = _configDao.getValueAndInitIfNotExist(Config.EncryptionIV.key(), + Config.EncryptionIV.getCategory(), getBase64EncodedRandomKey(128)); } return _encryptionIV; } - + @Override @DB public void resetEncryptionKeyIV() { - + SearchBuilder sb = _configDao.createSearchBuilder(); sb.and("name1", sb.entity().getName(), SearchCriteria.Op.EQ); sb.or("name2", sb.entity().getName(), SearchCriteria.Op.EQ); sb.done(); - + SearchCriteria sc = sb.create(); sc.setParameters("name1", Config.EncryptionKey.key()); sc.setParameters("name2", Config.EncryptionIV.key()); - + _configDao.expunge(sc); _encryptionKey = null; _encryptionIV = null; } - + private static String getBase64EncodedRandomKey(int nBits) { SecureRandom random; try { @@ -4056,4 +4070,15 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } } + + @Override + public List listDeploymentPlanners() { + List plannersAvailable = new ArrayList(); + for (DeploymentPlanner planner : _planners) { + plannersAvailable.add(planner.getName()); + } + + return plannersAvailable; + } + } diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index aa065294f8c..a3b731ab2a5 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -98,7 +98,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; @@ -402,9 +401,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; @@ -2836,7 +2832,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } return result; } - + @Override public boolean finalizeDeployment(Commands cmds, VirtualMachineProfile profile, DeployDestination dest, @@ -3036,7 +3032,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use + " stop due to exception ", ex); } } - + VMInstanceVO vm = profile.getVirtualMachine(); List nics = _nicDao.listByVmId(vm.getId()); for (NicVO nic : nics) { @@ -3174,15 +3170,15 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid()); - String plannerName = null; - for (DeployPlannerSelector dps : plannerSelectors) { - plannerName = dps.selectPlanner(vm); - if (plannerName != null) { - break; - } - } + // Get serviceOffering for Virtual Machine + ServiceOfferingVO offering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getServiceOfferingId()); + String plannerName = offering.getDeploymentPlanner(); if (plannerName == null) { - throw new CloudRuntimeException(String.format("cannot find DeployPlannerSelector for vm[uuid:%s, hypervisorType:%s]", vm.getUuid(), vm.getHypervisorType())); + if (vm.getHypervisorType() == HypervisorType.BareMetal) { + plannerName = "BareMetalPlanner"; + } else { + plannerName = _configDao.getValue(Config.VmDeploymentPlanner.key()); + } } String reservationId = vmEntity.reserve(plannerName, plan, new ExcludeList(), new Long(callerUser.getId()).toString()); @@ -3826,7 +3822,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use + cmd.getAccountName() + " is disabled."); } - //check caller has access to both the old and new account + //check caller has access to both the old and new account _accountMgr.checkAccess(caller, null, true, oldAccount); _accountMgr.checkAccess(caller, null, true, newAccount); diff --git a/server/test/com/cloud/resource/MockResourceManagerImpl.java b/server/test/com/cloud/resource/MockResourceManagerImpl.java index 5202c317e56..1fff3a63b1d 100644 --- a/server/test/com/cloud/resource/MockResourceManagerImpl.java +++ b/server/test/com/cloud/resource/MockResourceManagerImpl.java @@ -608,4 +608,10 @@ public class MockResourceManagerImpl extends ManagerBase implements ResourceMana return null; } + @Override + public boolean releaseHostReservation(Long hostId) { + // TODO Auto-generated method stub + return false; + } + } diff --git a/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java b/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java new file mode 100644 index 00000000000..e3b7d311ba7 --- /dev/null +++ b/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java @@ -0,0 +1,359 @@ +// 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.vm; + +import static org.junit.Assert.*; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import com.cloud.service.ServiceOfferingVO; +import com.cloud.storage.StorageManager; +import com.cloud.storage.dao.DiskOfferingDao; +import com.cloud.storage.dao.GuestOSCategoryDao; +import com.cloud.storage.dao.GuestOSDao; +import com.cloud.storage.dao.StoragePoolHostDao; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.capacity.CapacityManager; +import com.cloud.capacity.dao.CapacityDao; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.deploy.DeploymentPlanner.ExcludeList; +import com.cloud.agent.AgentManager; +import com.cloud.dc.ClusterDetailsDao; +import com.cloud.dc.ClusterVO; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.ClusterDao; +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.DeploymentClusterPlanner; +import com.cloud.deploy.DeploymentPlanner; +import com.cloud.deploy.DeploymentPlanner.PlannerResourceUsage; +import com.cloud.deploy.DeploymentPlanningManagerImpl; +import com.cloud.deploy.FirstFitPlanner; +import com.cloud.deploy.PlannerHostReservationVO; +import com.cloud.deploy.dao.PlannerHostReservationDao; +import org.apache.cloudstack.affinity.AffinityGroupProcessor; +import org.apache.cloudstack.affinity.dao.AffinityGroupDao; +import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +import org.apache.cloudstack.framework.messagebus.MessageBus; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.test.utils.SpringUtils; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.filter.TypeFilter; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; + +import com.cloud.exception.AffinityConflictException; +import com.cloud.exception.InsufficientServerCapacityException; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.user.AccountManager; +import com.cloud.utils.component.ComponentContext; +import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.dao.VMInstanceDao; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(loader = AnnotationConfigContextLoader.class) +public class DeploymentPlanningManagerImplTest { + + @Inject + DeploymentPlanningManagerImpl _dpm; + + @Inject + PlannerHostReservationDao _plannerHostReserveDao; + + @Inject VirtualMachineProfileImpl vmProfile; + + @Inject + AffinityGroupVMMapDao _affinityGroupVMMapDao; + + @Inject + ExcludeList avoids; + + @Inject + DataCenterVO dc; + + @Inject + DataCenterDao _dcDao; + + @Inject + FirstFitPlanner _planner; + + @Inject + ClusterDao _clusterDao; + + private static long domainId = 5L; + + private static long dataCenterId = 1L; + + + @BeforeClass + public static void setUp() throws ConfigurationException { + } + + @Before + public void testSetUp() { + ComponentContext.initComponentsLifeCycle(); + + PlannerHostReservationVO reservationVO = new PlannerHostReservationVO(200L, 1L, 2L, 3L, PlannerResourceUsage.Shared); + Mockito.when(_plannerHostReserveDao.persist(Mockito.any(PlannerHostReservationVO.class))).thenReturn(reservationVO); + Mockito.when(_plannerHostReserveDao.findById(Mockito.anyLong())).thenReturn(reservationVO); + Mockito.when(_affinityGroupVMMapDao.countAffinityGroupsForVm(Mockito.anyLong())).thenReturn(0L); + + VMInstanceVO vm = new VMInstanceVO(); + Mockito.when(vmProfile.getVirtualMachine()).thenReturn(vm); + + Mockito.when(_dcDao.findById(Mockito.anyLong())).thenReturn(dc); + Mockito.when(dc.getId()).thenReturn(dataCenterId); + + ClusterVO clusterVO = new ClusterVO(); + clusterVO.setHypervisorType(HypervisorType.XenServer.toString()); + Mockito.when(_clusterDao.findById(Mockito.anyLong())).thenReturn(clusterVO); + + Mockito.when(_planner.getName()).thenReturn("FirstFitPlanner"); + List planners = new ArrayList(); + planners.add(_planner); + _dpm.setPlanners(planners); + + } + + @Test + public void dataCenterAvoidTest() throws InsufficientServerCapacityException, AffinityConflictException { + ServiceOfferingVO svcOffering = new ServiceOfferingVO("testOffering", 1, 512, 500, 1, 1, false, false, false, + "test dpm", false, false, null, false, VirtualMachine.Type.User, domainId, null, "FirstFitPlanner"); + Mockito.when(vmProfile.getServiceOffering()).thenReturn(svcOffering); + + DataCenterDeployment plan = new DataCenterDeployment(dataCenterId); + + Mockito.when(avoids.shouldAvoid((DataCenterVO) Mockito.anyObject())).thenReturn(true); + DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids); + assertNull("DataCenter is in avoid set, destination should be null! ", dest); + } + + @Test + public void plannerCannotHandleTest() throws InsufficientServerCapacityException, AffinityConflictException { + ServiceOfferingVO svcOffering = new ServiceOfferingVO("testOffering", 1, 512, 500, 1, 1, false, false, false, + "test dpm", false, false, null, false, VirtualMachine.Type.User, domainId, null, + "UserDispersingPlanner"); + Mockito.when(vmProfile.getServiceOffering()).thenReturn(svcOffering); + + DataCenterDeployment plan = new DataCenterDeployment(dataCenterId); + Mockito.when(avoids.shouldAvoid((DataCenterVO) Mockito.anyObject())).thenReturn(false); + + Mockito.when(_planner.canHandle(vmProfile, plan, avoids)).thenReturn(false); + DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids); + assertNull("Planner cannot handle, destination should be null! ", dest); + } + + @Test + public void emptyClusterListTest() throws InsufficientServerCapacityException, AffinityConflictException { + ServiceOfferingVO svcOffering = new ServiceOfferingVO("testOffering", 1, 512, 500, 1, 1, false, false, false, + "test dpm", false, false, null, false, VirtualMachine.Type.User, domainId, null, "FirstFitPlanner"); + Mockito.when(vmProfile.getServiceOffering()).thenReturn(svcOffering); + + DataCenterDeployment plan = new DataCenterDeployment(dataCenterId); + Mockito.when(avoids.shouldAvoid((DataCenterVO) Mockito.anyObject())).thenReturn(false); + Mockito.when(_planner.canHandle(vmProfile, plan, avoids)).thenReturn(true); + + Mockito.when(((DeploymentClusterPlanner) _planner).orderClusters(vmProfile, plan, avoids)).thenReturn(null); + DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids); + assertNull("Planner cannot handle, destination should be null! ", dest); + } + + + @Configuration + @ComponentScan(basePackageClasses = { DeploymentPlanningManagerImpl.class }, includeFilters = { @Filter(value = TestConfiguration.Library.class, type = FilterType.CUSTOM) }, useDefaultFilters = false) + public static class TestConfiguration extends SpringUtils.CloudStackTestConfiguration { + + @Bean + public FirstFitPlanner firstFitPlanner() { + return Mockito.mock(FirstFitPlanner.class); + } + + @Bean + public DeploymentPlanner deploymentPlanner() { + return Mockito.mock(DeploymentPlanner.class); + } + + @Bean + public DataCenterVO dataCenter() { + return Mockito.mock(DataCenterVO.class); + } + + @Bean + public ExcludeList excludeList() { + return Mockito.mock(ExcludeList.class); + } + + @Bean + public VirtualMachineProfileImpl virtualMachineProfileImpl() { + return Mockito.mock(VirtualMachineProfileImpl.class); + } + + @Bean + public ClusterDetailsDao clusterDetailsDao() { + return Mockito.mock(ClusterDetailsDao.class); + } + + @Bean + public DataStoreManager cataStoreManager() { + return Mockito.mock(DataStoreManager.class); + } + + @Bean + public StorageManager storageManager() { + return Mockito.mock(StorageManager.class); + } + + @Bean + public HostDao hostDao() { + return Mockito.mock(HostDao.class); + } + + @Bean + public HostPodDao hostPodDao() { + return Mockito.mock(HostPodDao.class); + } + + @Bean + public ClusterDao clusterDao() { + return Mockito.mock(ClusterDao.class); + } + + @Bean + public GuestOSDao guestOSDao() { + return Mockito.mock(GuestOSDao.class); + } + + @Bean + public GuestOSCategoryDao guestOSCategoryDao() { + return Mockito.mock(GuestOSCategoryDao.class); + } + + @Bean + public CapacityManager capacityManager() { + return Mockito.mock(CapacityManager.class); + } + + @Bean + public StoragePoolHostDao storagePoolHostDao() { + return Mockito.mock(StoragePoolHostDao.class); + } + + @Bean + public VolumeDao volumeDao() { + return Mockito.mock(VolumeDao.class); + } + + @Bean + public ConfigurationDao configurationDao() { + return Mockito.mock(ConfigurationDao.class); + } + + @Bean + public DiskOfferingDao diskOfferingDao() { + return Mockito.mock(DiskOfferingDao.class); + } + + @Bean + public PrimaryDataStoreDao primaryDataStoreDao() { + return Mockito.mock(PrimaryDataStoreDao.class); + } + + @Bean + public CapacityDao capacityDao() { + return Mockito.mock(CapacityDao.class); + } + + @Bean + public PlannerHostReservationDao plannerHostReservationDao() { + return Mockito.mock(PlannerHostReservationDao.class); + } + + @Bean + public AffinityGroupProcessor affinityGroupProcessor() { + return Mockito.mock(AffinityGroupProcessor.class); + } + + @Bean + public AffinityGroupDao affinityGroupDao() { + return Mockito.mock(AffinityGroupDao.class); + } + + @Bean + public AffinityGroupVMMapDao affinityGroupVMMapDao() { + return Mockito.mock(AffinityGroupVMMapDao.class); + } + + @Bean + public AccountManager accountManager() { + return Mockito.mock(AccountManager.class); + } + + @Bean + public AgentManager agentManager() { + return Mockito.mock(AgentManager.class); + } + + @Bean + public MessageBus messageBus() { + return Mockito.mock(MessageBus.class); + } + + + @Bean + public UserVmDao userVMDao() { + return Mockito.mock(UserVmDao.class); + } + + @Bean + public VMInstanceDao vmInstanceDao() { + return Mockito.mock(VMInstanceDao.class); + } + + @Bean + public DataCenterDao dataCenterDao() { + return Mockito.mock(DataCenterDao.class); + } + + public static class Library implements TypeFilter { + + @Override + public boolean match(MetadataReader mdr, MetadataReaderFactory arg1) throws IOException { + ComponentScan cs = TestConfiguration.class.getAnnotation(ComponentScan.class); + return SpringUtils.includedInBasePackageClasses(mdr.getClassMetadata().getClassName(), cs); + } + } + } +} diff --git a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java index b64278c9709..ba18fa1c11d 100755 --- a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java +++ b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java @@ -431,7 +431,7 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu */ @Override public ServiceOfferingVO createServiceOffering(long userId, boolean isSystem, Type vm_typeType, String name, int cpu, int ramSize, int speed, String displayText, boolean localStorageRequired, boolean offerHA, - boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, Integer networkRate) { + boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, Integer networkRate, String deploymentPlanner) { // TODO Auto-generated method stub return null; } diff --git a/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java b/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java index 6f52397251b..7ffbe32d8bd 100644 --- a/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java +++ b/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java @@ -82,6 +82,7 @@ import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; import com.cloud.offerings.dao.NetworkOfferingServiceMapDaoImpl; import com.cloud.projects.ProjectManager; +import com.cloud.server.ManagementService; import com.cloud.service.dao.ServiceOfferingDaoImpl; import com.cloud.storage.dao.DiskOfferingDaoImpl; import com.cloud.storage.dao.S3DaoImpl; @@ -155,162 +156,167 @@ useDefaultFilters=false ) public class ChildTestConfiguration { - + + @Bean + public ManagementService managementService() { + return Mockito.mock(ManagementService.class); + } + @Bean public AccountManager acctMgr() { return Mockito.mock(AccountManager.class); } - + @Bean public NetworkService ntwkSvc() { return Mockito.mock(NetworkService.class); } - + @Bean public NetworkModel ntwkMdl() { return Mockito.mock(NetworkModel.class); } - + @Bean public AlertManager alertMgr() { return Mockito.mock(AlertManager.class); } - + @Bean public SecurityChecker securityChkr() { return Mockito.mock(SecurityChecker.class); } - + @Bean public ResourceLimitService resourceSvc() { return Mockito.mock(ResourceLimitService.class); } - + @Bean public ProjectManager projectMgr() { return Mockito.mock(ProjectManager.class); } - + @Bean public SecondaryStorageVmManager ssvmMgr() { return Mockito.mock(SecondaryStorageVmManager.class); } - + @Bean public SwiftManager swiftMgr() { return Mockito.mock(SwiftManager.class); } - + @Bean public S3Manager s3Mgr() { return Mockito.mock(S3Manager.class); } - + @Bean public VpcManager vpcMgr() { return Mockito.mock(VpcManager.class); } - + @Bean public UserVmDao userVMDao() { return Mockito.mock(UserVmDao.class); } - + @Bean public RulesManager rulesMgr() { return Mockito.mock(RulesManager.class); } - + @Bean public LoadBalancingRulesManager lbRulesMgr() { return Mockito.mock(LoadBalancingRulesManager.class); } - + @Bean public RemoteAccessVpnService vpnMgr() { return Mockito.mock(RemoteAccessVpnService.class); } - + @Bean public NetworkGuru ntwkGuru() { return Mockito.mock(NetworkGuru.class); } - + @Bean public NetworkElement ntwkElement() { return Mockito.mock(NetworkElement.class); } - + @Bean public IpDeployer ipDeployer() { return Mockito.mock(IpDeployer.class); } - + @Bean public DhcpServiceProvider dhcpProvider() { return Mockito.mock(DhcpServiceProvider.class); } - + @Bean public FirewallManager firewallMgr() { return Mockito.mock(FirewallManager.class); } - + @Bean public AgentManager agentMgr() { return Mockito.mock(AgentManager.class); } - + @Bean public StorageNetworkManager storageNtwkMgr() { return Mockito.mock(StorageNetworkManager.class); } - + @Bean public NetworkACLManager ntwkAclMgr() { return Mockito.mock(NetworkACLManager.class); } - + @Bean public Ipv6AddressManager ipv6Mgr() { return Mockito.mock(Ipv6AddressManager.class); } - + @Bean public ConfigurationDao configDao() { return Mockito.mock(ConfigurationDao.class); } - + @Bean public UserContext userContext() { return Mockito.mock(UserContext.class); } - + @Bean public UserContextInitializer userContextInitializer() { return Mockito.mock(UserContextInitializer.class); } - + @Bean public NetworkManager networkManager() { return Mockito.mock(NetworkManager.class); } - + @Bean public NetworkOfferingDao networkOfferingDao() { return Mockito.mock(NetworkOfferingDao.class); } - + @Bean public NetworkDao networkDao() { return Mockito.mock(NetworkDao.class); } - + @Bean public NetworkOfferingServiceMapDao networkOfferingServiceMapDao() { return Mockito.mock(NetworkOfferingServiceMapDao.class); } - + @Bean public DataCenterLinkLocalIpAddressDao datacenterLinkLocalIpAddressDao() { return Mockito.mock(DataCenterLinkLocalIpAddressDao.class); @@ -342,5 +348,5 @@ public class ChildTestConfiguration { } } - + } diff --git a/server/test/resources/affinityContext.xml b/server/test/resources/affinityContext.xml new file mode 100644 index 00000000000..ed880dd5648 --- /dev/null +++ b/server/test/resources/affinityContext.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index 334aae76f33..442a5446be5 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -973,9 +973,61 @@ CREATE TABLE `cloud`.`network_asa1000v_map` ( ALTER TABLE `cloud`.`network_offerings` ADD COLUMN `eip_associate_public_ip` int(1) unsigned NOT NULL DEFAULT 0 COMMENT 'true if public IP is associated with user VM creation by default when EIP service is enabled.' AFTER `elastic_ip_service`; --- Re-enable foreign key checking, at the end of the upgrade path -SET foreign_key_checks = 1; +CREATE TABLE `cloud`.`op_host_planner_reservation` ( + `id` bigint unsigned NOT NULL auto_increment, + `data_center_id` bigint unsigned NOT NULL, + `pod_id` bigint unsigned, + `cluster_id` bigint unsigned, + `host_id` bigint unsigned, + `resource_usage` varchar(255) COMMENT 'Shared(between planners) Vs Dedicated (exclusive usage to a planner)', + PRIMARY KEY (`id`), + INDEX `i_op_host_planner_reservation__host_resource_usage`(`host_id`, `resource_usage`), + CONSTRAINT `fk_planner_reservation__host_id` FOREIGN KEY (`host_id`) REFERENCES `host`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_planner_reservation__data_center_id` FOREIGN KEY (`data_center_id`) REFERENCES `cloud`.`data_center`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_planner_reservation__pod_id` FOREIGN KEY (`pod_id`) REFERENCES `cloud`.`host_pod_ref`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_planner_reservation__cluster_id` FOREIGN KEY (`cluster_id`) REFERENCES `cloud`.`cluster`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `cloud`.`service_offering` ADD COLUMN `deployment_planner` varchar(255) COMMENT 'Planner heuristics used to deploy a VM of this offering; if null global config vm.deployment.planner is used'; + +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'vm.deployment.planner', 'FirstFitPlanner', '[''FirstFitPlanner'', ''UserDispersingPlanner'', ''UserConcentratedPodPlanner'']: DeploymentPlanner heuristic that will be used for VM deployment.'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'host.reservation.release.period', '300000', 'The interval in milliseconds between host reservation release checks'); + +DROP VIEW IF EXISTS `cloud`.`service_offering_view`; +CREATE VIEW `cloud`.`service_offering_view` AS + select + service_offering.id, + disk_offering.uuid, + disk_offering.name, + disk_offering.display_text, + disk_offering.created, + disk_offering.tags, + disk_offering.removed, + disk_offering.use_local_storage, + disk_offering.system_use, + service_offering.cpu, + service_offering.speed, + service_offering.ram_size, + service_offering.nw_rate, + service_offering.mc_rate, + service_offering.ha_enabled, + service_offering.limit_cpu_use, + service_offering.host_tag, + service_offering.default_use, + service_offering.vm_type, + service_offering.sort_key, + service_offering.deployment_planner, + domain.id domain_id, + domain.uuid domain_uuid, + domain.name domain_name, + domain.path domain_path + from + `cloud`.`service_offering` + inner join + `cloud`.`disk_offering` ON service_offering.id = disk_offering.id + left join + `cloud`.`domain` ON disk_offering.domain_id = domain.id; -- Add "default" field to account/user tables ALTER TABLE `cloud`.`account` ADD COLUMN `default` int(1) unsigned NOT NULL DEFAULT '0' COMMENT '1 if account is default'; @@ -1605,3 +1657,8 @@ CREATE TABLE `cloud`.`nic_ip_alias` ( alter table `cloud`.`vpc_gateways` add column network_acl_id bigint unsigned default 1 NOT NULL; update `cloud`.`vpc_gateways` set network_acl_id = 2; + +-- Re-enable foreign key checking, at the end of the upgrade path +SET foreign_key_checks = 1; + + diff --git a/test/integration/smoke/test_deploy_vms_with_varied_deploymentplanners.py b/test/integration/smoke/test_deploy_vms_with_varied_deploymentplanners.py new file mode 100644 index 00000000000..d904a4cb7d8 --- /dev/null +++ b/test/integration/smoke/test_deploy_vms_with_varied_deploymentplanners.py @@ -0,0 +1,164 @@ +# 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. + +#!/usr/bin/env python + +import marvin +from marvin import cloudstackTestCase +from marvin.cloudstackTestCase import * + +import unittest +import hashlib +import random + +class TestDeployVmWithVariedPlanners(cloudstackTestCase): + """ + This test tests that we can create serviceOfferings with different deployment Planners and deploy virtual machines into a user account + using these service offerings and builtin template + """ + def setUp(self): + """ + CloudStack internally saves its passwords in md5 form and that is how we + specify it in the API. Python's hashlib library helps us to quickly hash + strings as follows + """ + mdf = hashlib.md5() + mdf.update('password') + mdf_pass = mdf.hexdigest() + + self.apiClient = self.testClient.getApiClient() #Get ourselves an API client + + self.acct = createAccount.createAccountCmd() #The createAccount command + self.acct.accounttype = 0 #We need a regular user. admins have accounttype=1 + self.acct.firstname = 'test' + self.acct.lastname = 'user' #What's up doc? + self.acct.username = 'testuser' + self.acct.password = mdf_pass #The md5 hashed password string + self.acct.email = 'test@domain.com' + self.acct.account = 'testacct' + self.acct.domainid = 1 #The default ROOT domain + self.acctResponse = self.apiClient.createAccount(self.acct) + # And upon successful creation we'll log a helpful message in our logs + # using the default debug logger of the test framework + self.debug("successfully created account: %s, id: \ + %s"%(self.acctResponse.name, \ + self.acctResponse.id)) + + #Create service offerings with varied planners + self.svcOfferingFirstFit = createServiceOffering.createServiceOfferingCmd() + self.svcOfferingFirstFit.name = 'Tiny Instance FirstFit' + self.svcOfferingFirstFit.displaytext = 'Tiny Instance with FirstFitPlanner' + self.svcOfferingFirstFit.cpuspeed = 100 + self.svcOfferingFirstFit.cpunumber = 1 + self.svcOfferingFirstFit.memory = 256 + self.svcOfferingFirstFit.deploymentplanner = 'FirstFitPlanner' + self.svcOfferingFirstFitResponse = self.apiClient.createServiceOffering(self.svcOfferingFirstFit) + + self.debug("successfully created serviceofferring name: %s, id: \ + %s, deploymentPlanner: %s"%(self.svcOfferingFirstFitResponse.name, \ + self.svcOfferingFirstFitResponse.id,self.svcOfferingFirstFitResponse.deploymentplanner)) + + #Create service offerings with varied planners + self.svcOfferingUserDispersing = createServiceOffering.createServiceOfferingCmd() + self.svcOfferingUserDispersing.name = 'Tiny Instance UserDispersing' + self.svcOfferingUserDispersing.displaytext = 'Tiny Instance with UserDispersingPlanner' + self.svcOfferingUserDispersing.cpuspeed = 100 + self.svcOfferingUserDispersing.cpunumber = 1 + self.svcOfferingUserDispersing.memory = 256 + self.svcOfferingUserDispersing.deploymentplanner = 'FirstFitPlanner' + self.svcOfferingUserDispersingResponse = self.apiClient.createServiceOffering(self.svcOfferingUserDispersing) + + self.debug("successfully created serviceofferring name: %s, id: \ + %s, deploymentPlanner: %s"%(self.svcOfferingUserDispersingResponse.name, \ + self.svcOfferingUserDispersingResponse.id,self.svcOfferingUserDispersingResponse.deploymentplanner)) + + def test_DeployVm(self): + """ + Let's start by defining the attributes of our VM that we will be + deploying on CloudStack. We will be assuming a single zone is available + and is configured and all templates are Ready + + The hardcoded values are used only for brevity. + """ + deployVmCmd = deployVirtualMachine.deployVirtualMachineCmd() + deployVmCmd.zoneid = 1 + deployVmCmd.account = self.acct.account + deployVmCmd.domainid = self.acct.domainid + deployVmCmd.templateid = 5 #For default template- CentOS 5.6(64 bit) + deployVmCmd.serviceofferingid = self.svcOfferingFirstFitResponse.id + + deployVmResponse = self.apiClient.deployVirtualMachine(deployVmCmd) + self.debug("VM %s was deployed in the job %s"%(deployVmResponse.id, deployVmResponse.jobid)) + + # At this point our VM is expected to be Running. Let's find out what + # listVirtualMachines tells us about VMs in this account + + listVmCmd = listVirtualMachines.listVirtualMachinesCmd() + listVmCmd.id = deployVmResponse.id + listVmResponse = self.apiClient.listVirtualMachines(listVmCmd) + + self.assertNotEqual(len(listVmResponse), 0, "Check if the list API \ + returns a non-empty response") + + vm1 = listVmResponse[0] + + self.assertEqual(vm1.id, deployVmResponse.id, "Check if the VM returned \ + is the same as the one we deployed") + self.assertEqual(vm1.state, "Running", "Check if VM has reached \ + a state of running") + + + deployVm2Cmd = deployVirtualMachine.deployVirtualMachineCmd() + deployVm2Cmd.zoneid = 1 + deployVm2Cmd.account = self.acct.account + deployVm2Cmd.domainid = self.acct.domainid + deployVm2Cmd.templateid = 5 #For default template- CentOS 5.6(64 bit) + deployVm2Cmd.serviceofferingid = self.svcOfferingFirstFitResponse.id + + deployVm2Response = self.apiClient.deployVirtualMachine(deployVm2Cmd) + self.debug("VM %s was deployed in the job %s"%(deployVm2Response.id, deployVm2Response.jobid)) + + # At this point our VM is expected to be Running. Let's find out what + # listVirtualMachines tells us about VMs in this account + + listVm2Cmd = listVirtualMachines.listVirtualMachinesCmd() + listVm2Cmd.id = deployVm2Response.id + listVm2Response = self.apiClient.listVirtualMachines(listVm2Cmd) + self.assertNotEqual(len(listVm2Response), 0, "Check if the list API \ + returns a non-empty response") + vm2 = listVm2Response[0] + self.assertEqual(vm2.id, deployVm2Response.id, "Check if the VM returned \ + is the same as the one we deployed") + self.assertEqual(vm2.state, "Running", "Check if VM has reached \ + a state of running") + + + def tearDown(self): # Teardown will delete the Account as well as the VM once the VM reaches "Running" state + """ + And finally let us cleanup the resources we created by deleting the + account. All good unittests are atomic and rerunnable this way + """ + deleteAcct = deleteAccount.deleteAccountCmd() + deleteAcct.id = self.acctResponse.id + self.apiClient.deleteAccount(deleteAcct) + deleteSvcOfferingFirstFit = deleteServiceOffering.deleteServiceOfferingCmd() + deleteSvcOfferingFirstFit.id = self.svcOfferingFirstFitResponse.id + self.apiClient.deleteServiceOffering(deleteSvcOfferingFirstFit); + deleteSvcOfferingUserDispersing = deleteServiceOffering.deleteServiceOfferingCmd() + deleteSvcOfferingUserDispersing.id = self.svcOfferingUserDispersingResponse.id + self.apiClient.deleteServiceOffering(deleteSvcOfferingUserDispersing); + \ No newline at end of file diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py index 8b6460e5507..375850304b7 100644 --- a/tools/apidoc/gen_toc.py +++ b/tools/apidoc/gen_toc.py @@ -142,6 +142,7 @@ known_categories = { 'listNics':'Nic', 'AffinityGroup': 'Affinity Group', 'InternalLoadBalancer': 'Internal LB', + 'DeploymentPlanners': 'Configuration', }