package org.ovirt.engine.core.bll; import org.ovirt.engine.core.bll.command.utils.StorageDomainSpaceChecker; import org.ovirt.engine.core.common.action.AddVmAndAttachToPoolParameters; import org.ovirt.engine.core.common.action.AddVmPoolWithVmsParameters; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.action.VdcReturnValueBase; import org.ovirt.engine.core.common.businessentities.DiskImageTemplate; import org.ovirt.engine.core.common.businessentities.StorageDomainStatus; import org.ovirt.engine.core.common.businessentities.StorageDomainType; import org.ovirt.engine.core.common.businessentities.VDSGroup; import org.ovirt.engine.core.common.businessentities.VmOsType; import org.ovirt.engine.core.common.businessentities.VmStatic; import org.ovirt.engine.core.common.businessentities.VmTemplate; import org.ovirt.engine.core.common.businessentities.storage_domains; import org.ovirt.engine.core.common.businessentities.vm_pools; import org.ovirt.engine.core.common.config.Config; import org.ovirt.engine.core.common.config.ConfigValues; import org.ovirt.engine.core.common.queries.IsVmWithSameNameExistParameters; import org.ovirt.engine.core.common.queries.VdcQueryType; import org.ovirt.engine.core.common.utils.ValidationUtils; import org.ovirt.engine.core.common.vdscommands.GetImageDomainsListVDSCommandParameters; import org.ovirt.engine.core.common.vdscommands.IrsBaseVDSCommandParameters; import org.ovirt.engine.core.common.vdscommands.VDSCommandType; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.compat.NGuid; import org.ovirt.engine.core.dal.VdcBllMessages; import org.ovirt.engine.core.dal.dbbroker.DbFacade; import org.ovirt.engine.core.dal.dbbroker.auditloghandling.CustomLogField; import org.ovirt.engine.core.dal.dbbroker.auditloghandling.CustomLogFields; import org.ovirt.engine.core.utils.linq.All; import org.ovirt.engine.core.utils.linq.LinqUtils; /** * This class responsible to create vmpool with vms within. This class not * transactive, that mean that function Execute not running in transaction. From * other hand, each vm added to system and attached to vmpool in transaction(one * transaction for two operation). To make it work, Transaction generated in * Execute function. Transactions isolated, that mean if one of vms not added * from some reason(image not exists, etc) - it not affect other vms generation * Each vm created with this format: {vm_name}_{number} where number runs from 1 * to vms count. If one of vms to be created already exists - number increased. * For example if vm_8 exists - vm_9 will be created instead of it. */ // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET // attributes: @CustomLogFields({ @CustomLogField("VmsCount") }) public abstract class CommonVmPoolWithVmsCommand<T extends AddVmPoolWithVmsParameters> extends AddVmPoolCommand<T> { /** * Constructor for command creation when compensation is applied on startup * * @param commandId */ protected CommonVmPoolWithVmsCommand(Guid commandId) { super(commandId); } public CommonVmPoolWithVmsCommand(T parameters) { super(parameters); setVmTemplateId(getParameters().getVmStaticData().getvmt_guid()); if (getVmTemplate() != null) { VmTemplateHandler.UpdateDisksFromDb(getVmTemplate()); } } public int getVmsCount() { return getParameters().getVmsCount(); } private boolean _addVmsSucceded = true; protected abstract Guid GetPoolId(); /** * This operation may take much time, so transactions timeout increased to 2 * minutes */ @Override protected void executeCommand() { Guid poolId = GetPoolId(); boolean isAtLeastOneVMCreationFailed = false; setActionReturnValue(poolId); VmTemplateHandler.lockVmTemplateInTransaction(getParameters().getVmStaticData().getvmt_guid(), getCompensationContext()); String vmName = getParameters().getVmStaticData().getvm_name(); int numChars = (Integer.toString(getParameters().getVmsCount())).length(); for (int i = 1, j = 1; i <= getParameters().getVmsCount(); i++, j++) { String currentVmName; j--; do { j++; int curChars = ((Integer) j).toString().length(); StringBuilder number = new StringBuilder(); for (int k = 0; k < numChars - curChars; k++) { number.append('0'); } number.append(j); currentVmName = String.format("%1$s-%2$s", vmName, number); } while ((Boolean) Backend .getInstance() .runInternalQuery(VdcQueryType.IsVmWithSameNameExist, new IsVmWithSameNameExistParameters(currentVmName)).getReturnValue()); VmStatic tempVar = new VmStatic(getParameters().getVmStaticData()); tempVar.setvm_name(currentVmName); VmStatic currVm = tempVar; AddVmAndAttachToPoolParameters tempVar2 = new AddVmAndAttachToPoolParameters(currVm, poolId, currentVmName, getStorageDomainId().getValue()); tempVar2.setSessionId(getParameters().getSessionId()); tempVar2.setParentCommand(VdcActionType.AddVmPoolWithVms); VdcReturnValueBase returnValue = Backend.getInstance().runInternalAction( VdcActionType.AddVmAndAttachToPool, tempVar2); if (returnValue != null && !returnValue.getSucceeded() && returnValue.getCanDoActionMessages().size() > 0) { for (String msg : returnValue.getCanDoActionMessages()) { if (!getReturnValue().getCanDoActionMessages().contains(msg)) { getReturnValue().getCanDoActionMessages().add(msg); } } _addVmsSucceded = returnValue.getSucceeded() && _addVmsSucceded; } isAtLeastOneVMCreationFailed = isAtLeastOneVMCreationFailed || !_addVmsSucceded; } getReturnValue().setCanDoAction(!isAtLeastOneVMCreationFailed); setSucceeded(!isAtLeastOneVMCreationFailed); VmTemplateHandler.UnLockVmTemplate(getParameters().getVmStaticData().getvmt_guid()); getCompensationContext().resetCompensation(); } public static boolean CanAddVmPoolWithVms(Object vmTemplateId, java.util.ArrayList<String> reasons, int vmsCount, Guid storagePoolId, Guid storageDomainId, int vmPriority) { return VmHandler.VerifyAddVm(reasons, vmsCount, vmTemplateId, storagePoolId, storageDomainId, true, true, vmPriority); } @Override protected boolean canDoAction() { addCanDoActionMessage(VdcBllMessages.VAR__TYPE__DESKTOP_POOL); if (!super.canDoAction()) { return false; } String vmPoolName = getParameters().getVmPool().getvm_pool_name(); if (vmPoolName == null || vmPoolName.isEmpty()) { addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_NAME_MAY_NOT_BE_EMPTY); return false; } else if (!isVmPoolNameValidLength(vmPoolName)) { addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_NAME_LENGTH_IS_TOO_LONG); return false; } else if (ValidationUtils.containsIlegalCharacters(vmPoolName)) { addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_NAME_MAY_NOT_CONTAIN_SPECIAL_CHARS); return false; } VDSGroup grp = DbFacade.getInstance().getVdsGroupDAO().get(getParameters().getVmPool().getvds_group_id()); if (grp == null) { addCanDoActionMessage(VdcBllMessages.VDS_CLUSTER_IS_NOT_VALID); return false; } VmStatic vmStaticData = getParameters().getVmStaticData(); if (!VmHandler.isMemorySizeLegal(vmStaticData.getos(), vmStaticData.getmem_size_mb(), getReturnValue().getCanDoActionMessages(), grp.getcompatibility_version().toString())) { return false; } vm_pools pool = DbFacade.getInstance().getVmPoolDAO().getByName(getParameters().getVmPool().getvm_pool_name()); if (pool != null && (getActionType() == VdcActionType.AddVmPoolWithVms || !pool.getvm_pool_id().equals( getParameters().getVmPoolId()))) { addCanDoActionMessage(VdcBllMessages.VM_POOL_CANNOT_CREATE_DUPLICATE_NAME); return false; } if (!((Boolean) Backend .getInstance() .getResourceManager() .RunVdsCommand(VDSCommandType.IsValid, new IrsBaseVDSCommandParameters(grp.getstorage_pool_id().getValue())).getReturnValue()) .booleanValue()) { addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_IMAGE_REPOSITORY_NOT_FOUND); return false; } if (!CanAddVmPoolWithVms(getParameters().getVmStaticData().getvmt_guid(), getReturnValue() .getCanDoActionMessages(), getParameters().getVmsCount(), grp.getstorage_pool_id() .getValue(), getStorageDomainId().getValue(), getParameters().getVmStaticData() .getpriority())) { return false; } if (getActionType() == VdcActionType.AddVmPoolWithVms && getParameters().getVmsCount() < 1) { addCanDoActionMessage(VdcBllMessages.VM_POOL_CANNOT_CREATE_WITH_NO_VMS); return false; } if (getParameters().getVmStaticData().getis_stateless()) { addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_VM_FROM_POOL_CANNOT_BE_STATELESS); return false; } return CheckFreeSpaceOnDestinationDomains(); } public boolean CheckFreeSpaceOnDestinationDomains() { boolean retValue = true; VmTemplate vmTemplate = DbFacade.getInstance().getVmTemplateDAO() .get(getParameters().getVmStaticData().getvmt_guid()); VmTemplateHandler.UpdateDisksFromDb(vmTemplate); double size = 0.0; java.util.ArrayList<Guid> domainsList; if (getStorageDomainId() == null || getStorageDomainId().getValue().equals(Guid.Empty)) { domainsList = (java.util.ArrayList<Guid>) Backend .getInstance() .getResourceManager() .RunVdsCommand( VDSCommandType.GetImageDomainsList, new GetImageDomainsListVDSCommandParameters(vmTemplate.getstorage_pool_id().getValue(), vmTemplate.getDiskList().get(0).getimage_group_id().getValue())).getReturnValue(); } else { domainsList = new java.util.ArrayList<Guid>(); domainsList.add(getStorageDomainId().getValue()); } for (Guid domainId : domainsList) { storage_domains domain = DbFacade.getInstance().getStorageDomainDAO().getForStoragePool(domainId, vmTemplate.getstorage_pool_id()); if (domain != null && domain.getstorage_domain_type() != StorageDomainType.ImportExport && domain.getstatus() == StorageDomainStatus.Active && domain.getavailable_disk_size() != null && StorageDomainSpaceChecker.hasSpaceForRequest(domain, getBlockSparseInitSizeInGB())) { size += domain.getavailable_disk_size() - getFreeSpaceCriticalLowInGB(); } } if (size < (getBlockSparseInitSizeInGB() * getParameters().getVmsCount() * vmTemplate.getDiskMap().size())) { retValue = false; addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_DISK_SPACE_LOW); } return retValue; } private Integer getFreeSpaceCriticalLowInGB() { return Config.<Integer> GetValue(ConfigValues.FreeSpaceCriticalLowInGB); } private int getBlockSparseInitSizeInGB() { return Config.<Integer> GetValue(ConfigValues.InitStorageSparseSizeInGB).intValue(); } // TODO: Need to think of balancing between vms of pool over storage domains @Override public NGuid getStorageDomainId() { if (getParameters().getStorageDomainId().equals(Guid.Empty) && getVmTemplate() != null // LINQ && VmTemplate.DiskMap.Values.First().image_guid != // VmTemplateHandler.BlankVmTemplateId) && !LinqUtils.firstOrNull(getVmTemplate().getDiskMap().values(), new All<DiskImageTemplate>()) .getId().equals(VmTemplateHandler.BlankVmTemplateId)) { getParameters().setStorageDomainId(AddVmCommand.SelectStorageDomain(getVmTemplate())); } return getParameters().getStorageDomainId(); } protected boolean getAddVmsSucceded() { return _addVmsSucceded; } /** * Check if the name of the VM-Pool has valid length, meaning it's not too * long. * * Since VMs in a pool are named like: 'SomePool_22', the max length allowed * for the name is the max VM-name length + room for the suffix: <Max Length * of VM name> - (length(<MaxVmsInPool>) + 1) * * In deciding the max length for a VM name, take into consideration if it's * a Windows or non-Windows VM * * @param vmPoolName * name of pool * * @return true if name has valid length; false if the name is too long */ protected boolean isVmPoolNameValidLength(String vmPoolName) { // get VM-pool OS type VmOsType osType = getParameters().getVmStaticData().getos(); // determine the max length considering the OS and the max-VMs-in-pool // get the max VM name (configuration parameter) int maxVmNameLengthWindows = Config.<Integer> GetValue(ConfigValues.MaxVmNameLengthWindows); int maxVmNameLengthNonWindows = Config.<Integer> GetValue(ConfigValues.MaxVmNameLengthNonWindows); int maxLength = osType.isWindows() ? maxVmNameLengthWindows : maxVmNameLengthNonWindows; Integer maxVmsInPool = Config.GetValue(ConfigValues.MaxVmsInPool); maxLength -= (String.valueOf(maxVmsInPool).length() + 1); // check if name is valid boolean nameLengthValid = (vmPoolName.length() <= maxLength); // return the result return nameLengthValid; } }