Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,17 @@
import com.cloud.exception.InsufficientServerCapacityException;
import com.cloud.utils.component.Manager;
import com.cloud.vm.VirtualMachineProfile;
import org.apache.cloudstack.framework.config.ConfigKey;

public interface DeploymentPlanningManager extends Manager {


static final ConfigKey<Boolean> allowRouterOnDisabledResource = new ConfigKey<Boolean>("Advanced", Boolean.class, "allow.router.on.disabled.resources", "false",
"Allow deploying VR in disabled Zones, Pods, and Clusters", true);

static final ConfigKey<Boolean> allowAdminVmOnDisabledResource = new ConfigKey<Boolean>("Advanced", Boolean.class, "allow.admin.vm.on.disabled.resources", "false",
"Allow deploying VMs owned by the admin account in disabled Clusters, Pods, and Zones", true);

/**
* Manages vm deployment stages: First Process Affinity/Anti-affinity - Call
* the chain of AffinityGroupProcessor adapters to set deploymentplan scope
Expand Down
13 changes: 13 additions & 0 deletions engine/schema/src/main/java/com/cloud/host/dao/HostDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,21 @@ public interface HostDao extends GenericDao<HostVO, Long>, StateDao<Status, Stat

List<HostVO> findByClusterId(Long clusterId);

/**
* Returns hosts that are 'Up' and 'Enabled' from the given Data Center/Zone
*/
List<HostVO> listByDataCenterId(long id);

/**
* Returns hosts that are from the given Data Center/Zone and at a given state (e.g. Creating, Enabled, Disabled, etc).
*/
List<HostVO> listByDataCenterIdAndState(long id, ResourceState state);

/**
* Returns hosts that are 'Up' and 'Disabled' from the given Data Center/Zone
*/
List<HostVO> listDisabledByDataCenterId(long id);

List<HostVO> listByDataCenterIdAndHypervisorType(long zoneId, Hypervisor.HypervisorType hypervisorType);

List<Long> listAllHosts(long zoneId);
Expand Down
20 changes: 17 additions & 3 deletions engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -463,13 +463,27 @@ public Integer countAllByTypeInZone(long zoneId, Type type) {

@Override
public List<HostVO> listByDataCenterId(long id) {
return listByDataCenterIdAndState(id, ResourceState.Enabled);
}

@Override
public List<HostVO> listByDataCenterIdAndState(long id, ResourceState state) {
SearchCriteria<HostVO> sc = scHostsFromZoneUpRouting(id);
sc.setParameters("resourceState", state);
return listBy(sc);
}

@Override
public List<HostVO> listDisabledByDataCenterId(long id) {
return listByDataCenterIdAndState(id, ResourceState.Disabled);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense to unify this to listByDataCentyerIdAndState() internally, no?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, not what i meant but looks good as well. ( i said internally ;)


private SearchCriteria<HostVO> scHostsFromZoneUpRouting(long id) {
SearchCriteria<HostVO> sc = DcSearch.create();
sc.setParameters("dc", id);
sc.setParameters("status", Status.Up);
sc.setParameters("type", Host.Type.Routing);
sc.setParameters("resourceState", ResourceState.Enabled);

return listBy(sc);
return sc;
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ public VMInstanceVO(long id, long serviceOfferingId, String name, String instanc
this.diskOfferingId = diskOfferingId;
}

protected VMInstanceVO() {
public VMInstanceVO() {
}

public Date getRemoved() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
import com.cloud.host.Host;
import com.cloud.host.Host.Type;
import com.cloud.host.dao.HostDao;
import com.cloud.org.Grouping;
import com.cloud.resource.ResourceManager;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.VolumeDao;
Expand Down Expand Up @@ -122,21 +121,7 @@ public List<Host> allocateTo(VirtualMachineProfile vm, DeploymentPlan plan, Type
}

for (PodCluster p : pcs) {
if (p.getPod().getAllocationState() != Grouping.AllocationState.Enabled) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Pod name: " + p.getPod().getName() + ", podId: " + p.getPod().getId() + " is in " + p.getPod().getAllocationState().name() +
" state, skipping this and trying other pods");
}
continue;
}
Long clusterId = p.getCluster() == null ? null : p.getCluster().getId();
if (p.getCluster() != null && p.getCluster().getAllocationState() != Grouping.AllocationState.Enabled) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Cluster name: " + p.getCluster().getName() + ", clusterId: " + clusterId + " is in " + p.getCluster().getAllocationState().name() +
" state, skipping this and trying other pod-clusters");
}
continue;
}
DataCenterDeployment newPlan = new DataCenterDeployment(plan.getDataCenterId(), p.getPod().getId(), clusterId, null, null, null);
hosts = super.allocateTo(vm, newPlan, type, avoid, returnUpTo);
if (hosts != null && !hosts.isEmpty()) {
Expand Down
153 changes: 122 additions & 31 deletions server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@
import javax.naming.ConfigurationException;

import org.apache.cloudstack.affinity.AffinityGroupDomainMapVO;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.user.AccountVO;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.StringUtils;
import com.cloud.exception.StorageUnavailableException;
import com.cloud.utils.db.Filter;
import com.cloud.utils.fsm.StateMachine2;

import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.log4j.Logger;
import org.apache.cloudstack.affinity.AffinityGroupProcessor;
import org.apache.cloudstack.affinity.AffinityGroupService;
import org.apache.cloudstack.affinity.AffinityGroupVMMapVO;
Expand All @@ -50,9 +64,6 @@
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.utils.identity.ManagementServerNode;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.log4j.Logger;

import com.cloud.agent.AgentManager;
import com.cloud.agent.Listener;
Expand Down Expand Up @@ -84,7 +95,6 @@
import com.cloud.exception.AffinityConflictException;
import com.cloud.exception.ConnectionException;
import com.cloud.exception.InsufficientServerCapacityException;
import com.cloud.exception.StorageUnavailableException;
import com.cloud.gpu.GPU;
import com.cloud.host.DetailVO;
import com.cloud.host.Host;
Expand All @@ -97,7 +107,6 @@
import com.cloud.org.Cluster;
import com.cloud.org.Grouping;
import com.cloud.resource.ResourceManager;
import com.cloud.resource.ResourceState;
import com.cloud.service.ServiceOfferingDetailsVO;
import com.cloud.service.dao.ServiceOfferingDetailsDao;
import com.cloud.storage.DiskOfferingVO;
Expand All @@ -107,31 +116,26 @@
import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePool;
import com.cloud.storage.StoragePoolHostVO;
import com.cloud.storage.VMTemplateVO;
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.VMTemplateDao;
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.StringUtils;
import com.cloud.utils.component.Manager;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallback;
import com.cloud.utils.db.TransactionStatus;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.fsm.StateListener;
import com.cloud.utils.fsm.StateMachine2;
import com.cloud.vm.DiskProfile;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine;
Expand All @@ -144,12 +148,14 @@
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;

public class DeploymentPlanningManagerImpl extends ManagerBase implements DeploymentPlanningManager, Manager, Listener,
StateListener<State, VirtualMachine.Event, VirtualMachine> {
StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {

private static final Logger s_logger = Logger.getLogger(DeploymentPlanningManagerImpl.class);
@Inject
AgentManager _agentMgr;
@Inject
private AccountDao accountDao;
@Inject
protected UserVmDao _vmDao;
@Inject
protected VMInstanceDao _vmInstanceDao;
Expand Down Expand Up @@ -177,6 +183,7 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy
@Inject
private VMTemplateDao templateDao;

private static final long ADMIN_ACCOUNT_ROLE_ID = 1l;
private static final long INITIAL_RESERVATION_RELEASE_CHECKER_DELAY = 30L * 1000L; // thirty seconds expressed in milliseconds
protected long _nodeId = -1;

Expand Down Expand Up @@ -283,6 +290,8 @@ public DeployDestination planDeployment(VirtualMachineProfile vmProfile, Deploym
s_logger.debug("Is ROOT volume READY (pool already allocated)?: " + (plan.getPoolId() != null ? "Yes" : "No"));
}

avoidDisabledResources(vmProfile, dc, avoids);

String haVmTag = (String)vmProfile.getParameter(VirtualMachineProfile.Param.HaTag);
String uefiFlag = (String)vmProfile.getParameter(VirtualMachineProfile.Param.UefiFlag);

Expand Down Expand Up @@ -311,17 +320,8 @@ public DeployDestination planDeployment(VirtualMachineProfile vmProfile, Deploym
}

Pod pod = _podDao.findById(host.getPodId());
// check if the cluster or the pod is disabled
if (pod.getAllocationState() != Grouping.AllocationState.Enabled) {
s_logger.warn("The Pod containing this host is in disabled state, PodId= " + pod.getId());
return null;
}

Cluster cluster = _clusterDao.findById(host.getClusterId());
if (cluster.getAllocationState() != Grouping.AllocationState.Enabled) {
s_logger.warn("The Cluster containing this host is in disabled state, PodId= " + cluster.getId());
return null;
}

boolean displayStorage = getDisplayStorageFromVmProfile(vmProfile);
if (vm.getHypervisorType() == HypervisorType.BareMetal) {
Expand Down Expand Up @@ -422,8 +422,15 @@ public DeployDestination planDeployment(VirtualMachineProfile vmProfile, Deploym
s_logger.debug("The last host of this VM does not have required GPU devices available");
}
} else {
if (host.getStatus() == Status.Up && host.getResourceState() == ResourceState.Enabled) {
if (checkVmProfileAndHost(vmProfile, host)) {
if (host.getStatus() == Status.Up) {
boolean hostTagsMatch = true;
if(offering.getHostTag() != null){
_hostDao.loadHostTags(host);
if (!(host.getHostTags() != null && host.getHostTags().contains(offering.getHostTag()))) {
hostTagsMatch = false;
}
}
if (hostTagsMatch) {
long cluster_id = host.getClusterId();
ClusterDetailsVO cluster_detail_cpu = _clusterDetailsDao.findDetail(cluster_id,
"cpuOvercommitRatio");
Expand Down Expand Up @@ -573,6 +580,86 @@ private boolean getDisplayStorageFromVmProfile(VirtualMachineProfile vmProfile)
return vmProfile == null || vmProfile.getTemplate() == null || !vmProfile.getTemplate().isDeployAsIs();
}

/**
* Adds disabled resources (Data centers, Pods, Clusters, and hosts) to exclude list (avoid) in case of disabled state.
*/
public void avoidDisabledResources(VirtualMachineProfile vmProfile, DataCenter dc, ExcludeList avoids) {
if (vmProfile.getType().isUsedBySystem() && isRouterDeployableInDisabledResources()) {
return;
}

VMInstanceVO vm = _vmInstanceDao.findById(vmProfile.getId());
AccountVO owner = accountDao.findById(vm.getAccountId());
boolean isOwnerRoleIdAdmin = false;

if (owner != null && owner.getRoleId() != null && owner.getRoleId() == ADMIN_ACCOUNT_ROLE_ID) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DaanHoogland I fixed the issue raised on tests regarding VMs deployed on a project.

Manual tests are looking good now; thanks :-)

isOwnerRoleIdAdmin = true;
}

if (isOwnerRoleIdAdmin && isAdminVmDeployableInDisabledResources()) {
return;
}

avoidDisabledDataCenters(dc, avoids);
avoidDisabledPods(dc, avoids);
avoidDisabledClusters(dc, avoids);
avoidDisabledHosts(dc, avoids);
}

/**
* Returns the value of the ConfigKey 'allow.router.on.disabled.resources'.
* @note this method allows mocking and testing with the respective ConfigKey parameter.
*/
protected boolean isRouterDeployableInDisabledResources() {
return allowRouterOnDisabledResource.value();
}

/**
* Returns the value of the ConfigKey 'allow.admin.vm.on.disabled.resources'.
* @note this method allows mocking and testing with the respective ConfigKey parameter.
*/
protected boolean isAdminVmDeployableInDisabledResources() {
return allowAdminVmOnDisabledResource.value();
}

/**
* Adds disabled Hosts to the ExcludeList in order to avoid them at the deployment planner.
*/
protected void avoidDisabledHosts(DataCenter dc, ExcludeList avoids) {
List<HostVO> disabledHosts = _hostDao.listDisabledByDataCenterId(dc.getId());
for (HostVO host : disabledHosts) {
avoids.addHost(host.getId());
}
}

/**
* Adds disabled Clusters to the ExcludeList in order to avoid them at the deployment planner.
*/
protected void avoidDisabledClusters(DataCenter dc, ExcludeList avoids) {
List<Long> pods = _podDao.listAllPods(dc.getId());
for (Long podId : pods) {
List<Long> disabledClusters = _clusterDao.listDisabledClusters(dc.getId(), podId);
avoids.addClusterList(disabledClusters);
}
}

/**
* Adds disabled Pods to the ExcludeList in order to avoid them at the deployment planner.
*/
protected void avoidDisabledPods(DataCenter dc, ExcludeList avoids) {
List<Long> disabledPods = _podDao.listDisabledPods(dc.getId());
avoids.addPodList(disabledPods);
}

/**
* Adds disabled Data Centers (Zones) to the ExcludeList in order to avoid them at the deployment planner.
*/
protected void avoidDisabledDataCenters(DataCenter dc, ExcludeList avoids) {
if (dc.getAllocationState() == Grouping.AllocationState.Disabled) {
avoids.addDataCenter(dc.getId());
}
}

@Override
public DeploymentPlanner getDeploymentPlannerByName(String plannerName) {
if (plannerName != null) {
Expand Down Expand Up @@ -1092,11 +1179,6 @@ private DeployDestination checkClustersforDestination(List<Long> clusterList, Vi
for (Long clusterId : clusterList) {
ClusterVO clusterVO = _clusterDao.findById(clusterId);

if (clusterVO.getAllocationState() == Grouping.AllocationState.Disabled && !plan.isMigrationPlan()) {
s_logger.debug("Cannot deploy in disabled cluster " + clusterId + ", skipping this cluster");
avoid.addCluster(clusterVO.getId());
}

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());
Expand All @@ -1110,7 +1192,9 @@ private DeployDestination checkClustersforDestination(List<Long> clusterList, Vi
new DataCenterDeployment(plan.getDataCenterId(), clusterVO.getPodId(), clusterVO.getId(), null, plan.getPoolId(), null, plan.getReservationContext());

Pod pod = _podDao.findById(clusterVO.getPodId());
if (pod.getAllocationState() == Grouping.AllocationState.Enabled ) {
if (CollectionUtils.isNotEmpty(avoid.getPodsToAvoid()) && avoid.getPodsToAvoid().contains(pod.getId())) {
s_logger.debug("The cluster is in a disabled pod : " + pod.getId());
} else {
// find suitable hosts under this cluster, need as many hosts as we
// get.
List<Host> suitableHosts = findSuitableHosts(vmProfile, potentialPlan, avoid, HostAllocator.RETURN_UPTO_ALL);
Expand Down Expand Up @@ -1151,9 +1235,6 @@ private DeployDestination checkClustersforDestination(List<Long> clusterList, Vi
s_logger.debug("No suitable hosts found under this Cluster: " + clusterId);
}
}
else {
s_logger.debug("The cluster is in a disabled pod : " + pod.getId());
}

if (canAvoidCluster(clusterVO, avoid, plannerAvoidOutput, vmProfile)) {
avoid.addCluster(clusterVO.getId());
Expand Down Expand Up @@ -1739,4 +1820,14 @@ public boolean postStateTransitionEvent(StateMachine2.Transition<State, Event> t
}
return true;
}

@Override
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {allowRouterOnDisabledResource, allowAdminVmOnDisabledResource};
}

@Override
public String getConfigComponentName() {
return DeploymentPlanningManager.class.getSimpleName();
}
}
Loading