package org.ovirt.engine.core.bll;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.MaintananceNumberOfVdssParameters;
import org.ovirt.engine.core.common.action.MaintananceVdsParameters;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.businessentities.AsyncTaskStatus;
import org.ovirt.engine.core.common.businessentities.MigrationSupport;
import org.ovirt.engine.core.common.businessentities.VDS;
import org.ovirt.engine.core.common.businessentities.VDSGroup;
import org.ovirt.engine.core.common.businessentities.VDSStatus;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.VdsSpmStatus;
import org.ovirt.engine.core.common.businessentities.network;
import org.ovirt.engine.core.common.errors.VdcBLLException;
import org.ovirt.engine.core.common.errors.VdcBllErrors;
import org.ovirt.engine.core.common.vdscommands.SetVdsStatusVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.VDSCommandType;
import org.ovirt.engine.core.common.vdscommands.VdsIdVDSCommandParametersBase;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.compat.LogCompat;
import org.ovirt.engine.core.compat.LogFactoryCompat;
import org.ovirt.engine.core.dal.VdcBllMessages;
import org.ovirt.engine.core.dal.dbbroker.DbFacade;
//VB & C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes:
@InternalCommandAttribute
@NonTransactiveCommandAttribute
public class MaintananceNumberOfVdssCommand<T extends MaintananceNumberOfVdssParameters> extends CommandBase<T> {
private java.util.ArrayList<Guid> _vdsGroupIds;
private final Map<Guid, VdcObjectType> inspectedEntitiesMap;
public MaintananceNumberOfVdssCommand(T parameters) {
super(parameters);
Iterable<Guid> vdsIdList = getParameters().getVdsIdList();
inspectedEntitiesMap = new HashMap<Guid, VdcObjectType>();
for (Guid g : vdsIdList) {
inspectedEntitiesMap.put(g, VdcObjectType.VDS);
}
}
private void MoveVdssToGoingToMaintananceMode() {
for (VDS vds : vdssToMaintenance.values()) {
if (vds.getstatus() != VDSStatus.PreparingForMaintenance && vds.getstatus() != VDSStatus.NonResponsive
&& vds.getstatus() != VDSStatus.Down) {
Backend.getInstance()
.getResourceManager()
.RunVdsCommand(
VDSCommandType.SetVdsStatus,
new SetVdsStatusVDSCommandParameters(vds.getvds_id(), VDSStatus.PreparingForMaintenance));
}
}
}
private void MigrateAllVdss() {
for (Guid vdsId : getParameters().getVdsIdList()) {
// ParametersCurrentUser = CurrentUser
MaintananceVdsParameters tempVar = new MaintananceVdsParameters(vdsId, getParameters().getIsInternal());
tempVar.setSessionId(getParameters().getSessionId());
VdcReturnValueBase result = Backend.getInstance().runInternalAction(VdcActionType.MaintananceVds, tempVar);
if (!result.getCanDoAction()) {
getReturnValue().getCanDoActionMessages().addAll(result.getCanDoActionMessages());
getReturnValue().setCanDoAction(false);
}
}
}
@Override
protected void executeCommand() {
MoveVdssToGoingToMaintananceMode();
MigrateAllVdss();
// set network to operational / non-operational
for (Guid id : _vdsGroupIds) {
List<network> networks = DbFacade.getInstance().getNetworkDAO().getAllForCluster(id);
for (network net : networks) {
AttachNetworkToVdsGroupCommand.SetNetworkStatus(id, net);
}
}
setSucceeded(true);
}
private final java.util.HashMap<Guid, VDS> vdssToMaintenance = new java.util.HashMap<Guid, VDS>();
@Override
protected boolean canDoAction() {
boolean result = true;
_vdsGroupIds = new java.util.ArrayList<Guid>();
Set<Guid> vdsWithRunningVMs = new HashSet<Guid>();
List<String> hostNotRespondingList = new ArrayList<String>();
List<String> hostsWithNonMigratableVms = new ArrayList<String>();
List<String> nonMigratableVms = new ArrayList<String>();
// check if one of the target vdss is spm, if so check that there are no
// tasks running
for (Guid vdsId : getParameters().getVdsIdList()) {
VDS vds = DbFacade.getInstance().getVdsDAO().get(vdsId);
if (vds == null) {
log.error(String.format("ResourceManager::vdsMaintenance could not find vds_id = '%1$s'", vdsId));
addCanDoActionMessage(VdcBllMessages.VDS_INVALID_SERVER_ID);
result = false;
} else {
List<VM> vms = DbFacade.getInstance().getVmDAO().getAllRunningForVds(vdsId);
if (vms.size() > 0) {
vdsWithRunningVMs.add(vdsId);
}
_vdsGroupIds.add(vds.getvds_group_id());
List<String> nonMigratableVmDescriptionsToFrontEnd = new ArrayList<String>();
for (VM vm : vms) {
if (vm.getMigrationSupport() != MigrationSupport.MIGRATABLE) {
nonMigratableVmDescriptionsToFrontEnd.add(vm.getvm_name());
}
}
if (nonMigratableVmDescriptionsToFrontEnd.size() > 0) {
hostsWithNonMigratableVms.add(vds.getvds_name());
nonMigratableVms.addAll(nonMigratableVmDescriptionsToFrontEnd);
// The non migratable VM names will be comma separated
log.error(String.format("VDS %1$s contains non migratable VMs", vdsId));
result = false;
} else if (vds.getstatus() == VDSStatus.Maintenance) {
addCanDoActionMessage(VdcBllMessages.VDS_CANNOT_MAINTENANCE_VDS_IS_IN_MAINTENANCE);
result = false;
} else if (vds.getspm_status() == VdsSpmStatus.Contending) {
addCanDoActionMessage(VdcBllMessages.VDS_CANNOT_MAINTENANCE_SPM_CONTENDING);
result = false;
} else if (vds.getstatus() == VDSStatus.NonResponsive && vds.getvm_count() > 0) {
result = false;
hostNotRespondingList.add(vds.getvds_name());
} else if (vds.getstatus() == VDSStatus.NonResponsive && vds.getspm_status() != VdsSpmStatus.None) {
result = false;
addCanDoActionMessage(VdcBllMessages.VDS_CANNOT_MAINTENANCE_VDS_IS_NOT_RESPONDING_AND_SPM);
} else if (vds.getspm_status() == VdsSpmStatus.SPM && vds.getstatus() == VDSStatus.Up) {
try {
java.util.HashMap<Guid, AsyncTaskStatus> taskStatuses =
(java.util.HashMap<Guid, AsyncTaskStatus>) Backend
.getInstance()
.getResourceManager()
.RunVdsCommand(VDSCommandType.HSMGetAllTasksStatuses,
new VdsIdVDSCommandParametersBase(vdsId)).getReturnValue();
if (taskStatuses.size() > 0) {
addCanDoActionMessage(VdcBllMessages.VDS_CANNOT_MAINTENANCE_SPM_WITH_RUNNING_TASKS);
result = false;
}
} catch (VdcBLLException e) {
if (e.getErrorCode() == VdcBllErrors.VDS_NETWORK_ERROR) {
addCanDoActionMessage(VdcBllMessages.VDS_CANNOT_MAINTENANCE_VDS_IS_NOT_RESPONDING_AND_SPM);
result = false;
} else {
log.error("Error getting spm task list.", e);
}
} catch (RuntimeException exp) {
log.error("Error getting spm task list.", exp);
}
}
if (!vdssToMaintenance.containsKey(vdsId)) {
vdssToMaintenance.put(vdsId, vds);
}
}
}
// If one of the host is non responsive with running VM's, add a CanDoAction message.
handleNonResponsiveHosts(hostNotRespondingList);
// If one of the vms is non migratable, add a CanDoAction message.
handleNonMigratableVms(hostsWithNonMigratableVms, nonMigratableVms);
if (result) {
// Remove all redundant clusters in clusters list, by adding it to a
// set.
// For each cluster check for each host that belongs to it, if its a
// part of the parameters and
// if there are running hosts for it - if it is up and is not in the
// parameters -migration will be possible
// to be performed, and there is no point to continue the check for
// the given cluster - otherwise,
// if the host is up and in the parameters - it may be that the
// cluster is problematic (no hosts in up
// state that we will be able to migrate VMs to)
// In the end - if the clusters list is not empty - this is an
// error, use the "problematic clusters list" to format an error to
// the client
Set<Guid> clustersAsSet = new HashSet<Guid>();
clustersAsSet.addAll(_vdsGroupIds);
List<String> problematicClusters = new ArrayList<String>();
List<String> allHostsWithRunningVms = new ArrayList<String>();
for (Guid clusterID : clustersAsSet) {
boolean candidateForProblematicCluster = false;
List<VDS> vdsList = DbFacade.getInstance().getVdsDAO().getAllForVdsGroup(clusterID);
boolean vdsForMigrationExists =
checkIfThereIsVDSToHoldMigratedVMs(getParameters().getVdsIdList(), vdsList);
if (!vdsForMigrationExists) {
List<String> candidateHostsWithRunningVms = new ArrayList<String>();
for (VDS vdsInCluster : vdsList) {
if (vdsWithRunningVMs.contains(vdsInCluster.getvds_id())) {
candidateHostsWithRunningVms.add(vdsInCluster.getvds_name());
}
}
// Passed on all vds in cluster - if map is not empty (host found with VMs) -
// this is indeed a problematic
// cluster
if (!candidateHostsWithRunningVms.isEmpty()) {
addClusterDetails(clusterID, problematicClusters);
allHostsWithRunningVms.addAll(candidateHostsWithRunningVms);
}
}
}
// If there are problematic clusters
result = problematicClusters.isEmpty();
if (!result) {
addCanDoActionMessage(VdcBllMessages.CANNOT_MAINTANANCE_VDS_RUN_VMS_NO_OTHER_RUNNING_VDS);
String commaDelimitedClusters = StringUtils.join(problematicClusters, ",");
getReturnValue().getCanDoActionMessages().add(String.format("$ClustersList %1$s",
commaDelimitedClusters));
getReturnValue().getCanDoActionMessages().add(String.format("$HostsList %1$s",
StringUtils.join(allHostsWithRunningVms, ",")));
}
}
return result;
}
/**
* If found hosts which has non-migratable VM's on them, add the host names and Vm's to the message.
*
* @param hostNotRespondingList
* - List of non responsive hosts with running VM's on them.
*/
private void handleNonMigratableVms(List<String> hostsWithNonMigratableVms, List<String> nonMigratableVms) {
if (!hostsWithNonMigratableVms.isEmpty()) {
addCanDoActionMessage(VdcBllMessages.VDS_CANNOT_MAINTENANCE_IT_INCLUDES_NON_MIGRATABLE_VM);
getReturnValue().getCanDoActionMessages().add((String.format("$VmsList %1$s",
StringUtils.join(nonMigratableVms, " , "))));
getReturnValue().getCanDoActionMessages().add((String.format("$HostsList %1$s",
StringUtils.join(hostsWithNonMigratableVms, " , "))));
}
}
private boolean checkIfThereIsVDSToHoldMigratedVMs(Iterable<Guid> vdsIDListFromParams, List<VDS> vdsInCluster) {
// Checks if the number of UP VDS in the parameters for a given cluster
// is
// less than the number of UP parameters in that cluster - if this is
// the case, we have
// a VDS to migrate to
Set<Guid> upVdsIDsInCluster = new HashSet<Guid>();
for (VDS vds : vdsInCluster) {
if (vds.getstatus() == VDSStatus.Up) {
upVdsIDsInCluster.add(vds.getvds_id());
}
}
int numOfUpVDSInClusterAndParams = 0;
for (Guid vdsID : vdsIDListFromParams) {
if (upVdsIDsInCluster.contains(vdsID)) {
numOfUpVDSInClusterAndParams++;
}
}
return numOfUpVDSInClusterAndParams < upVdsIDsInCluster.size();
}
/**
* If found hosts which are non-responsive and has VM's on them, add the host names to the message.
*
* @param hostNotRespondingList
* - List of non responsive hosts with running VM's on them.
*/
private void handleNonResponsiveHosts(List<String> hostNotRespondingList) {
if (!hostNotRespondingList.isEmpty()) {
addCanDoActionMessage(VdcBllMessages.VDS_CANNOT_MAINTENANCE_VDS_IS_NOT_RESPONDING_WITH_VMS);
getReturnValue().getCanDoActionMessages().add(String.format("$HostNotResponding %1$s",
StringUtils.join(hostNotRespondingList, ",")));
}
}
private void addClusterDetails(Guid vdsGroupID, List<String> clustersWithRunningVms) {
if (vdsGroupID != null && !vdsGroupID.equals(Guid.Empty)) {
VDSGroup vdsGroup = DbFacade.getInstance().getVdsGroupDAO().getWithRunningVms(vdsGroupID);
if (vdsGroup != null) {
clustersWithRunningVms.add(vdsGroup.getname());
}
}
}
private static LogCompat log = LogFactoryCompat.getLog(MaintananceNumberOfVdssCommand.class);
@Override
public Map<Guid, VdcObjectType> getPermissionCheckSubjects() {
return Collections.unmodifiableMap(inspectedEntitiesMap);
}
}