package org.ovirt.engine.core.bll;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.job.ExecutionContext;
import org.ovirt.engine.core.bll.quota.QuotaClusterConsumptionParameter;
import org.ovirt.engine.core.bll.quota.QuotaConsumptionParameter;
import org.ovirt.engine.core.bll.quota.QuotaVdsDependent;
import org.ovirt.engine.core.bll.tasks.CommandCoordinatorUtil;
import org.ovirt.engine.core.bll.tasks.interfaces.CommandCallback;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.AttachUserToVmFromPoolAndRunParameters;
import org.ovirt.engine.core.common.action.LockProperties;
import org.ovirt.engine.core.common.action.LockProperties.Scope;
import org.ovirt.engine.core.common.action.PermissionsOperationsParameters;
import org.ovirt.engine.core.common.action.RunVmParams;
import org.ovirt.engine.core.common.action.VdcActionParametersBase.EndProcedure;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.asynctasks.EntityInfo;
import org.ovirt.engine.core.common.businessentities.Permission;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.aaa.DbUser;
import org.ovirt.engine.core.common.errors.EngineMessage;
import org.ovirt.engine.core.common.job.Step;
import org.ovirt.engine.core.common.job.StepEnum;
import org.ovirt.engine.core.common.locks.LockingGroup;
import org.ovirt.engine.core.common.utils.Pair;
import org.ovirt.engine.core.compat.CommandStatus;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.compat.TransactionScopeOption;
import org.ovirt.engine.core.dal.job.ExecutionMessageDirector;
import org.ovirt.engine.core.dao.DbUserDao;
import org.ovirt.engine.core.dao.PermissionDao;
import org.ovirt.engine.core.dao.VmDao;
import org.ovirt.engine.core.utils.lock.EngineLock;
public class AttachUserToVmFromPoolAndRunCommand<T extends AttachUserToVmFromPoolAndRunParameters>
extends VmPoolUserCommandBase<T> implements QuotaVdsDependent {
@Inject
private VmPoolHandler vmPoolHandler;
@Inject
private PermissionDao permissionDao;
@Inject
private DbUserDao dbUserDao;
@Inject
private VmDao vmDao;
protected AttachUserToVmFromPoolAndRunCommand(Guid commandId) {
super(commandId);
}
public AttachUserToVmFromPoolAndRunCommand(T parameters, CommandContext commandContext) {
super(parameters, commandContext);
}
@Override
protected void init() {
super.init();
if (Guid.Empty.equals(getParameters().getVmId()) && getVmPool() != null) {
boolean vmPrestarted = true;
Guid vmToAttach = vmPoolHandler.selectPrestartedVm(
getVmPoolId(),
getVmPool().isStateful(),
(vmId, errors) -> getReturnValue().getValidationMessages().addAll(errors));
if (Guid.Empty.equals(vmToAttach)) {
vmPrestarted = false;
vmToAttach = vmPoolHandler.selectNonPrestartedVm(
getVmPoolId(),
(vmId, errors) -> getReturnValue().getValidationMessages().addAll(errors));
}
getParameters().setVmId(vmToAttach);
getParameters().setEntityInfo(new EntityInfo(VdcObjectType.VM, vmToAttach));
getParameters().setVmPrestarted(vmPrestarted);
}
setVmId(getParameters().getVmId());
}
@Override
protected boolean validate() {
if (getVmPool() == null) {
return failValidation(EngineMessage.VM_POOL_NOT_FOUND);
}
if (Guid.Empty.equals(getVmId())) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_NO_AVAILABLE_POOL_VMS);
}
long vmCount = countVmsAssignedToUser();
int limit = getVmPool().getMaxAssignedVmsPerUser();
if (vmCount >= limit) {
return failValidation(EngineMessage.VM_POOL_CANNOT_ATTACH_TO_MORE_VMS_FROM_POOL);
}
return true;
}
private long countVmsAssignedToUser() {
return vmDao.getAllForUser(getAdUserId())
.stream()
.filter(vm -> vm.getVmPoolId() != null && getVmPoolId().equals(vm.getVmPoolId()))
.count();
}
@Override
protected void setActionMessageParameters() {
addValidationMessage(EngineMessage.VAR__ACTION__ALLOCATE_AND_RUN);
addValidationMessage(EngineMessage.VAR__TYPE__VM_FROM_VM_POOL);
}
@Override
public Guid getVmId() {
return getParameters().getVmId();
}
@Override
public void setVmId(Guid value) {
super.setVmId(value);
getParameters().setVmId(value);
}
private boolean isVmPrestarted() {
return getParameters().isVmPrestarted();
}
@Override
public Guid getAdUserId() {
return getParameters().getUserId();
}
@Override
protected TransactionScopeOption getTransactionScopeOption() {
return getActionState() != CommandActionState.EXECUTE ? TransactionScopeOption.Suppress
: super.getTransactionScopeOption();
}
private void initPoolUser() {
DbUser user = getDbUser();
if (user != null && user.getId() == null) {
user.setId(Guid.newGuid());
dbUserDao.save(user);
}
}
@Override
protected void executeCommand() {
initPoolUser();
VdcReturnValueBase vdcReturnValue = attachUserToVm();
if (!vdcReturnValue.getSucceeded()) {
log.info("Failed to give user '{}' permission to Vm '{}'", getAdUserId(), getVmId());
setActionReturnValue(vdcReturnValue);
return;
} else {
log.info("Succeeded giving user '{}' permission to Vm '{}'", getAdUserId(), getVmId());
}
if (!isVmPrestarted()) {
// Only when using a VM that is not prestarted we need to run the VM
vdcReturnValue = runVm();
setSucceeded(vdcReturnValue.getSucceeded());
getReturnValue().getVdsmTaskIdList().addAll(getReturnValue().getInternalVdsmTaskIdList());
} else {
// No need to start, just return it
setSucceeded(true);
}
setActionReturnValue(getVmId());
}
private VdcReturnValueBase attachUserToVm() {
Permission perm = new Permission(getAdUserId(),
PredefinedRoles.ENGINE_USER.getId(),
getVmId(),
VdcObjectType.VM);
PermissionsOperationsParameters permParams = new PermissionsOperationsParameters(perm);
permParams.setShouldBeLogged(false);
permParams.setParentCommand(getActionType());
permParams.setParentParameters(getParameters());
return runInternalAction(VdcActionType.AddPermission,
permParams,
cloneContext().withoutExecutionContext().withoutLock());
}
private ExecutionContext createRunVmContext() {
ExecutionContext ctx = new ExecutionContext();
try {
Step step = executionHandler.addSubStep(getExecutionContext(),
getExecutionContext().getJob().getStep(StepEnum.EXECUTING),
StepEnum.TAKING_VM_FROM_POOL,
ExecutionMessageDirector.resolveStepMessage(StepEnum.TAKING_VM_FROM_POOL,
Collections.singletonMap(VdcObjectType.VM.name().toLowerCase(), getVmName())));
ctx.setStep(step);
ctx.setMonitored(true);
ctx.setShouldEndJob(true);
} catch (RuntimeException e) {
log.error("Error when creating executing context for running stateless VM", e);
}
return ctx;
}
private VdcReturnValueBase runVm() {
RunVmParams runVmParams = new RunVmParams(getVmId());
runVmParams.setSessionId(getParameters().getSessionId());
runVmParams.setEntityInfo(new EntityInfo(VdcObjectType.VM, getVmId()));
runVmParams.setParentCommand(getActionType());
runVmParams.setParentParameters(getParameters());
runVmParams.setEndProcedure(EndProcedure.COMMAND_MANAGED);
runVmParams.setRunAsStateless(!getVmPool().isStateful());
ExecutionContext runVmContext = createRunVmContext();
VdcReturnValueBase vdcReturnValue = runInternalAction(VdcActionType.RunVm,
runVmParams,
cloneContext().withExecutionContext(runVmContext).withCompensationContext(null));
getTaskIdList().addAll(vdcReturnValue.getInternalVdsmTaskIdList());
return vdcReturnValue;
}
private RunVmParams getChildRunVmParameters() {
if (getParameters().getImagesParameters() == null) {
return null;
}
return (RunVmParams) getParameters().getImagesParameters()
.stream()
.filter(p -> p instanceof RunVmParams)
.findFirst()
.orElse(null);
}
private boolean isRunVmSucceeded() {
RunVmParams runVmParams = getChildRunVmParameters();
return runVmParams == null
|| CommandCoordinatorUtil.getCommandEntity(runVmParams.getCommandId()).getReturnValue().getSucceeded();
}
@Override
protected void endSuccessfully() {
if (!Guid.Empty.equals(getVmId())) {
if (!isRunVmSucceeded()) {
log.warn("endSuccessfully: RunVm failed, detaching user from VM");
detachUserFromVmFromPool();
getReturnValue().setEndActionTryAgain(false);
}
} else {
setCommandShouldBeLogged(false);
log.warn("endSuccessfully: VM is not set");
}
setSucceeded(true);
}
@Override
protected void endWithFailure() {
if (!Guid.Empty.equals(getVmId())) {
log.warn("endWithFailure: RunVm failed, detaching user from VM");
detachUserFromVmFromPool();
} else {
log.warn("endWithFailure: VM is not set");
}
setSucceeded(true);
}
@Override
public AuditLogType getAuditLogTypeValue() {
switch (getActionState()) {
case EXECUTE:
return getSucceeded() ? AuditLogType.USER_ATTACH_USER_TO_VM_FROM_POOL
: AuditLogType.USER_ATTACH_USER_TO_VM_FROM_POOL_FAILED;
case END_SUCCESS:
return getSucceeded() ? AuditLogType.USER_ATTACH_USER_TO_VM_FROM_POOL_FINISHED_SUCCESS
: AuditLogType.USER_ATTACH_USER_TO_VM_FROM_POOL_FINISHED_FAILURE;
default:
return AuditLogType.USER_ATTACH_USER_TO_VM_FROM_POOL_FINISHED_FAILURE;
}
}
private void detachUserFromVmFromPool() {
if (!Guid.Empty.equals(getAdUserId())) {
Permission perm = permissionDao
.getForRoleAndAdElementAndObject(
PredefinedRoles.ENGINE_USER.getId(),
getAdUserId(),
getVmId());
if (perm != null) {
permissionDao.remove(perm.getId());
}
}
}
@Override
protected void freeCustomLocks() {
if (getCommandStatus() == CommandStatus.ENDED_WITH_FAILURE && !Guid.Empty.equals(getVmId())
&& !getParameters().isVmPrestarted()) {
EngineLock runLock = vmPoolHandler.createLock(getVmId());
lockManager.releaseLock(runLock);
log.info("VM lock freed: {}", runLock);
}
}
@Override
protected LockProperties applyLockProperties(LockProperties lockProperties) {
return lockProperties.withScope(Scope.Execution);
}
@Override
protected Map<String, Pair<String, String>> getExclusiveLocks() {
Map<String, Pair<String, String>> locks = new HashMap<>();
locks.put(getAdUserId().toString(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.USER_VM_POOL, EngineMessage.ACTION_TYPE_FAILED_OBJECT_LOCKED));
if (!Guid.Empty.equals(getVmId()) && isVmPrestarted()) {
EngineLock runLock = vmPoolHandler.createLock(getVmId());
if (runLock.getExclusiveLocks() != null) {
locks.putAll(runLock.getExclusiveLocks());
}
}
return locks;
}
@Override
protected Map<String, Pair<String, String>> getSharedLocks() {
if (!Guid.Empty.equals(getVmId()) && isVmPrestarted()) {
return vmPoolHandler.createLock(getVmId()).getSharedLocks();
} else {
return null;
}
}
@Override
public List<QuotaConsumptionParameter> getQuotaVdsConsumptionParameters() {
List<QuotaConsumptionParameter> list = new ArrayList<>();
VM vm = getVm();
if (vm != null) {
setStoragePoolId(vm.getStoragePoolId());
list.add(new QuotaClusterConsumptionParameter(vm.getQuotaId(),
null,
QuotaConsumptionParameter.QuotaAction.CONSUME,
vm.getClusterId(),
vm.getCpuPerSocket() * vm.getNumOfSockets(),
vm.getMemSizeMb()));
}
return list;
}
@Override
public CommandCallback getCallback() {
return new ConcurrentChildCommandsExecutionCallback();
}
}