package org.ovirt.engine.core.bll; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.ovirt.engine.core.bll.command.utils.StorageDomainSpaceChecker; import org.ovirt.engine.core.common.AuditLogType; import org.ovirt.engine.core.common.VdcObjectType; import org.ovirt.engine.core.common.action.CreateSnapshotFromTemplateParameters; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.action.VdcReturnValueBase; import org.ovirt.engine.core.common.action.VmManagementParametersBase; import org.ovirt.engine.core.common.businessentities.DiskImage; import org.ovirt.engine.core.common.businessentities.DiskImageBase; import org.ovirt.engine.core.common.businessentities.DiskImageTemplate; import org.ovirt.engine.core.common.businessentities.MigrationSupport; 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.VMStatus; import org.ovirt.engine.core.common.businessentities.VmDynamic; import org.ovirt.engine.core.common.businessentities.VmInterfaceType; import org.ovirt.engine.core.common.businessentities.VmNetworkInterface; import org.ovirt.engine.core.common.businessentities.VmStatic; import org.ovirt.engine.core.common.businessentities.VmStatistics; import org.ovirt.engine.core.common.businessentities.VmTemplate; import org.ovirt.engine.core.common.businessentities.permissions; import org.ovirt.engine.core.common.businessentities.storage_domains; import org.ovirt.engine.core.common.config.Config; import org.ovirt.engine.core.common.config.ConfigValues; import org.ovirt.engine.core.common.errors.VdcBLLException; import org.ovirt.engine.core.common.errors.VdcBllErrors; import org.ovirt.engine.core.common.queries.IsVmWithSameNameExistParameters; import org.ovirt.engine.core.common.queries.VdcQueryType; import org.ovirt.engine.core.common.validation.group.CreateEntity; import org.ovirt.engine.core.common.vdscommands.GetImageDomainsListVDSCommandParameters; import org.ovirt.engine.core.common.vdscommands.VDSCommandType; 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.compat.NGuid; import org.ovirt.engine.core.compat.RefObject; import org.ovirt.engine.core.dal.VdcBllMessages; import org.ovirt.engine.core.dal.dbbroker.DbFacade; import org.ovirt.engine.core.utils.linq.All; import org.ovirt.engine.core.utils.linq.Function; import org.ovirt.engine.core.utils.linq.LinqUtils; import org.ovirt.engine.core.utils.transaction.TransactionMethod; import org.ovirt.engine.core.utils.transaction.TransactionSupport; import org.ovirt.engine.core.utils.vmproperties.VmPropertiesUtils; import org.ovirt.engine.core.utils.vmproperties.VmPropertiesUtils.VMCustomProperties; import org.ovirt.engine.core.utils.vmproperties.VmPropertiesUtils.ValidationError; public class AddVmCommand<T extends VmManagementParametersBase> extends VmManagementCommandBase<T> { public AddVmCommand(T parameters) { super(parameters); // if we came from EndAction the VmId is not null super.setVmId((parameters.getVmId().equals(Guid.Empty)) ? Guid.NewGuid() : parameters.getVmId()); parameters.setVmId(super.getVmId()); setVmTemplateId(parameters.getVmStaticData().getvmt_guid()); if (getVmTemplate() != null) { VmTemplateHandler.UpdateDisksFromDb(getVmTemplate()); } parameters.setEntityId(getVmId()); if (getParameters() != null) { if (getVdsGroup() != null) { setStoragePoolId(getVdsGroup().getstorage_pool_id() != null ? getVdsGroup().getstorage_pool_id() .getValue() : Guid.Empty); } } } protected AddVmCommand(Guid commandId) { super(commandId); } protected String newMac = ""; @Override public NGuid getStorageDomainId() { if (getParameters() != null) { // LINQ && VmTemplate.DiskMap.Values.First().image_guid != // VmTemplateHandler.BlankVmTemplateId) if (getParameters().getStorageDomainId().equals(Guid.Empty) && getVmTemplate() != null && getVmTemplate().getDiskMap().size() > 0 && !LinqUtils.firstOrNull(getVmTemplate().getDiskMap().values(), new All<DiskImageTemplate>()) .getId().equals(VmTemplateHandler.BlankVmTemplateId)) { getParameters().setStorageDomainId(SelectStorageDomain(getVmTemplate())); } return getParameters().getStorageDomainId(); } else { return null; } } /** * Select storage domain according to the given template - with most * available disk size. must initialize template disks before using this * method * * @param vmTemplate * @return */ public static Guid SelectStorageDomain(VmTemplate vmTemplate) { Guid selectedDomainId = Guid.Empty; if (vmTemplate != null && vmTemplate.getDiskMap().size() > 0) { int size = -1; // DiskImage disk = null; // LINQ // DbFacade.Instance.GetSnapshotById(vmTemplate.DiskMap.Values.First().image_guid); DiskImage disk = DbFacade.getInstance().getDiskImageDAO().getSnapshotById( LinqUtils.firstOrNull(vmTemplate.getDiskMap().values(), new All<DiskImageTemplate>()) .getId()); java.util.ArrayList<Guid> domainsList = (java.util.ArrayList<Guid>) Backend .getInstance() .getResourceManager() .RunVdsCommand( VDSCommandType.GetImageDomainsList, new GetImageDomainsListVDSCommandParameters(vmTemplate.getstorage_pool_id().getValue(), disk.getimage_group_id().getValue())).getReturnValue(); 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 && StorageDomainSpaceChecker.isBelowThresholds(domain)) { if (domain.getavailable_disk_size() != null && domain.getavailable_disk_size() > size) { selectedDomainId = domainId; size = domain.getavailable_disk_size(); } } } } return selectedDomainId; } private Guid _vmSnapshotId = Guid.Empty; protected Guid getVmSnapshotId() { if (_vmSnapshotId.equals(Guid.Empty)) { _vmSnapshotId = Guid.NewGuid(); } return _vmSnapshotId; } protected List<VmNetworkInterface> _vmInterfaces; protected List<VmNetworkInterface> getVmInterfaces() { if (_vmInterfaces == null) { _vmInterfaces = ((DbFacade.getInstance().getVmNetworkInterfaceDAO().getAllForTemplate(getVmTemplate().getId())) != null) ? DbFacade .getInstance().getVmNetworkInterfaceDAO().getAllForTemplate(getVmTemplate().getId()) : new java.util.ArrayList<VmNetworkInterface>(); } return _vmInterfaces; } protected List<DiskImageBase> _vmDisks; protected List<DiskImageBase> getVmDisks() { if (_vmDisks == null) { // LINQ 29456 // _vmDisks = // DbFacade.Instance.getImageTemplateByVmt(VmTemplateId).Select(a => // (DiskImageBase)DbFacade.Instance.GetSnapshotById(a.image_guid)).ToList(); _vmDisks = LinqUtils.foreach(DbFacade.getInstance() .getDiskImageTemplateDAO() .getAllByVmTemplate(getVmTemplateId()), new Function<DiskImageTemplate, DiskImageBase>() { @Override public DiskImageBase eval(DiskImageTemplate diskImageTemplate) { return DbFacade.getInstance() .getDiskImageDAO() .getSnapshotById(diskImageTemplate.getId()); } }); } return _vmDisks; } public boolean CanAddVm(Object vmTemplateId, java.util.ArrayList<String> reasons) { VmStatic vmStaticFromParams = getParameters().getVmStaticData(); boolean returnValue = CanAddVm(vmTemplateId, reasons, 1, vmStaticFromParams.getvm_name(), getStoragePoolId() .getValue(), vmStaticFromParams.getpriority()); // check that template image and vm are on the same storage pool // LINQ && VmTemplate.DiskMap.Values.First().image_guid != // VmTemplateHandler.BlankVmTemplateId) if (returnValue) { List<ValidationError> validationErrors = VmPropertiesUtils.validateVMProperties(vmStaticFromParams); if (!validationErrors.isEmpty()) { handleCustomPropertiesError(validationErrors, reasons); returnValue = false; } } if (returnValue && getVmTemplate().getDiskMap().size() > 0 && !LinqUtils.firstOrNull(getVmTemplate().getDiskMap().values(), new All<DiskImageTemplate>()) .getId().equals(VmTemplateHandler.BlankVmTemplateId)) { storage_domains domain = DbFacade.getInstance().getStorageDomainDAO().get( getStorageDomainId().getValue()); if (!StorageDomainSpaceChecker.isBelowThresholds(domain)) { returnValue = false; reasons.add(VdcBllMessages.ACTION_TYPE_FAILED_DISK_SPACE_LOW.toString()); } else if (!getStoragePoolId().equals(getVmTemplate().getstorage_pool_id().getValue())) { reasons.add(VdcBllMessages.ACTION_TYPE_FAILED_STORAGE_POOL_NOT_MATCH.toString()); returnValue = false; } else if (domain.getStorageDynamicData() != null) { // populate template disks for domain size check VmTemplateHandler.UpdateDisksFromDb(getVmTemplate()); returnValue = StorageDomainSpaceChecker.hasSpaceForRequest(domain, getNeededDiskSize()); if (!returnValue) reasons.add(VdcBllMessages.ACTION_TYPE_FAILED_DISK_SPACE_LOW.toString()); } } // Check id dedicated host is same as VM cluster if (returnValue) { returnValue = isDedicatedVdsOnSameCluster(vmStaticFromParams); } return returnValue; } protected boolean CanDoAddVmCommand() { boolean returnValue = false; returnValue = areParametersLegal(getReturnValue().getCanDoActionMessages()); returnValue = returnValue && CheckPCIAndIDELimit(getParameters().getVmStaticData().getnum_of_monitors(), getVmInterfaces(), getVmDisks(), getReturnValue().getCanDoActionMessages()) && CanAddVm(getVmTemplateId(), getReturnValue().getCanDoActionMessages()) && hostToRunExist(); return returnValue; } protected boolean hostToRunExist() { if (getParameters().getVmStaticData().getdedicated_vm_for_vds() != null) { if (DbFacade.getInstance().getVdsDAO().get(getParameters().getVmStaticData().getdedicated_vm_for_vds()) == null) { addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_HOST_NOT_EXIST); return false; } } return true; } public static boolean CheckCpuSockets(int num_of_sockets, int cpu_per_socket, String compatibility_version, java.util.ArrayList<String> CanDoActionMessages) { boolean retValue = true; if (retValue && (num_of_sockets * cpu_per_socket) > Config.<Integer> GetValue(ConfigValues.MaxNumOfVmCpus, compatibility_version)) { CanDoActionMessages.add(VdcBllMessages.ACTION_TYPE_FAILED_MAX_NUM_CPU.toString()); retValue = false; } if (retValue && num_of_sockets > Config.<Integer> GetValue(ConfigValues.MaxNumOfVmSockets, compatibility_version)) { CanDoActionMessages.add(VdcBllMessages.ACTION_TYPE_FAILED_MAX_NUM_SOCKETS.toString()); retValue = false; } if (retValue && cpu_per_socket > Config.<Integer> GetValue(ConfigValues.MaxNumOfCpuPerSocket, compatibility_version)) { CanDoActionMessages.add(VdcBllMessages.ACTION_TYPE_FAILED_MAX_CPU_PER_SOCKET.toString()); retValue = false; } if (retValue && cpu_per_socket < 1) { CanDoActionMessages.add(VdcBllMessages.ACTION_TYPE_FAILED_MIN_CPU_PER_SOCKET.toString()); retValue = false; } if (retValue && num_of_sockets < 1) { CanDoActionMessages.add(VdcBllMessages.ACTION_TYPE_FAILED_MIN_NUM_SOCKETS.toString()); retValue = false; } return retValue; } @Override protected List<Class<?>> getValidationGroups() { addValidationGroup(CreateEntity.class); return super.getValidationGroups(); } @Override protected void setActionMessageParameters() { addCanDoActionMessage(VdcBllMessages.VAR__ACTION__ADD); addCanDoActionMessage(VdcBllMessages.VAR__TYPE__VM); } @Override protected boolean canDoAction() { boolean returnValue = CanDoAddVmCommand(); String vmName = getParameters().getVm().getvm_name(); if (vmName == null || vmName.isEmpty()) { returnValue = false; addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_NAME_MAY_NOT_BE_EMPTY); } else { // check that VM name is not too long boolean vmNameValidLength = isVmNameValidLength(getParameters().getVm()); if (!vmNameValidLength) { returnValue = false; addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_NAME_LENGTH_IS_TOO_LONG); } else if (returnValue && getStorageDomainId() != null) { storage_domains storage = DbFacade.getInstance().getStorageDomainDAO().getForStoragePool( getStorageDomainId().getValue(), getStoragePoolId()); if (storage.getstorage_domain_type() == StorageDomainType.ImportExport || storage.getstorage_domain_type() == StorageDomainType.ISO) { returnValue = false; addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_STORAGE_DOMAIN_TYPE_ILLEGAL); } } } if (returnValue && Config.<Boolean> GetValue(ConfigValues.LimitNumberOfNetworkInterfaces, getVdsGroup().getcompatibility_version().toString())) { // check that we have no more then 8 interfaces (kvm limitation in version 2.x) if (!validateNumberOfNics(getVmInterfaces(), null)) { addCanDoActionMessage(VdcBllMessages.NETWORK_INTERFACE_EXITED_MAX_INTERFACES); returnValue = false; } } if (returnValue) { returnValue = AddVmCommand.CheckCpuSockets(getParameters().getVmStaticData().getnum_of_sockets(), getParameters().getVmStaticData().getcpu_per_socket(), getVdsGroup().getcompatibility_version() .toString(), getReturnValue().getCanDoActionMessages()); } return returnValue; } public boolean CanAddVm(Object vmTemplateId, java.util.ArrayList<String> reasons, int vmsCount, String name, Guid storagePoolId, int vmPriority) { boolean returnValue; // Checking if a desktop with same name already exists boolean exists = (Boolean) Backend.getInstance() .runInternalQuery(VdcQueryType.IsVmWithSameNameExist, new IsVmWithSameNameExistParameters(name)) .getReturnValue(); if (exists) { if (reasons != null) { reasons.add(VdcBllMessages.ACTION_TYPE_FAILED_VM_ALREADY_EXIST.toString()); } return false; } boolean checkTemplateLock = getParameters().getParentCommand() == VdcActionType.AddVmPoolWithVms ? false : true; returnValue = VmHandler.VerifyAddVm(reasons, vmsCount, vmTemplateId, storagePoolId, getStorageDomainId() .getValue(), !getParameters().getDontCheckTemplateImages(), checkTemplateLock, vmPriority); return returnValue; } @Override protected void ExecuteVmCommand() { ArrayList<String> errorMessages = new ArrayList<String>(); if (CanAddVm(getVmTemplateId(), errorMessages)) { TransactionSupport.executeInNewTransaction(new TransactionMethod<Void>() { @Override public Void runInTransaction() { AddVmStatic(); AddVmDynamic(); AddVmNetwork(); AddVmStatistics(); getCompensationContext().stateChanged(); return null; } }); addVmPermission(); if (AddVmImages()) { setActionReturnValue(getVm().getvm_guid()); setSucceeded(true); } // TODO not in use - all default tag attaching should be removed /* * if (!getParameters().getDontAttachToDefaultTag()) { * AttachTagToUser(); } */ } else { log.errorFormat("Failed to add vm . The reasons are: {0}", StringUtils.join(errorMessages, ',')); } } protected static boolean IsLegalClusterId(Guid clusterId, java.util.ArrayList<String> reasons) { // check given cluster id VDSGroup vdsGroup = DbFacade.getInstance().getVdsGroupDAO().get(clusterId); boolean legalClusterId = (vdsGroup != null); if (!legalClusterId) { reasons.add(VdcBllErrors.VM_INVALID_SERVER_CLUSTER_ID.toString()); } return legalClusterId; } protected boolean areParametersLegal(java.util.ArrayList<String> reasons) { boolean returnValue = false; VmStatic vmStaticData = getParameters().getVmStaticData(); if (vmStaticData != null) { returnValue = vmStaticData.getMigrationSupport() != MigrationSupport.PINNED_TO_HOST || !vmStaticData.getauto_startup(); if (!returnValue) { reasons.add(VdcBllMessages.ACTION_TYPE_FAILED_VM_CANNOT_BE_HIGHLY_AVAILABLE_AND_PINNED_TO_HOST .toString()); } returnValue = returnValue && IsLegalClusterId(vmStaticData.getvds_group_id(), reasons); returnValue = returnValue && VmHandler.isMemorySizeLegal(vmStaticData.getos(), vmStaticData.getmem_size_mb(), reasons, getVdsGroup().getcompatibility_version().toString()); } return returnValue; } protected void AddVmNetwork() { // Add interfaces from template for (VmNetworkInterface iface : getVmInterfaces()) { String mac = null; RefObject<String> tempRefObject = new RefObject<String>(mac); MacPoolManager.getInstance().allocateNewMac(tempRefObject); mac = tempRefObject.argvalue; iface.setId(Guid.NewGuid()); iface.setMacAddress(mac); iface.setSpeed(VmInterfaceType.forValue(iface.getType()).getSpeed()); iface.setVmTemplateId(null); iface.setVmId(getParameters().getVmStaticData().getId()); DbFacade.getInstance().getVmNetworkInterfaceDAO().save(iface); getCompensationContext().snapshotNewEntity(iface); DbFacade.getInstance().getVmNetworkStatisticsDAO().save(iface.getStatistics()); getCompensationContext().snapshotNewEntity(iface.getStatistics()); } } protected void AddVmStatic() { VmStatic vmStatic = getParameters().getVmStaticData(); vmStatic.setId(getVmId()); vmStatic.setcreation_date(getNow()); // Parses the custom properties field that was filled by frontend to // predefined and user defined fields if (vmStatic.getCustomProperties() != null) { VMCustomProperties properties = VmPropertiesUtils.parseProperties(vmStatic.getCustomProperties()); String predefinedProperties = properties.getPredefinedProperties(); String userDefinedProperties = properties.getUseDefinedProperties(); vmStatic.setPredefinedProperties(predefinedProperties); vmStatic.setUserDefinedProperties(userDefinedProperties); } DbFacade.getInstance().getVmStaticDAO().save(vmStatic); getCompensationContext().snapshotNewEntity(vmStatic); } private void AddVmDynamic() { VmDynamic tempVar = new VmDynamic(); tempVar.setId(getVmId()); tempVar.setstatus(VMStatus.Down); tempVar.setvm_host(""); tempVar.setvm_ip(""); tempVar.setdisplay_type(getParameters().getVmStaticData().getdefault_display_type()); VmDynamic vmDynamic = tempVar; DbFacade.getInstance().getVmDynamicDAO().save(vmDynamic); getCompensationContext().snapshotNewEntity(vmDynamic); } private void AddVmStatistics() { VmStatistics stats = new VmStatistics(); stats.setId(getVmId()); DbFacade.getInstance().getVmStatisticsDAO().save(stats); getCompensationContext().snapshotNewEntity(stats); } protected boolean AddVmImages() { if (getVmTemplate().getDiskMap().size() > 0) { if (getVm().getstatus() != VMStatus.Down) { log.error("Cannot add images. VM is not Down"); throw new VdcBLLException(VdcBllErrors.IRS_IMAGE_STATUS_ILLEGAL); } VmHandler.LockVm(getVmId()); for (DiskImageTemplate dit : getVmTemplate().getDiskMap().values()) { CreateSnapshotFromTemplateParameters tempVar = new CreateSnapshotFromTemplateParameters( dit.getit_guid(), getParameters().getVmStaticData().getId()); tempVar.setStorageDomainId(getStorageDomainId().getValue()); tempVar.setVmSnapshotId(getVmSnapshotId()); tempVar.setParentCommand(VdcActionType.AddVm); tempVar.setEntityId(getParameters().getEntityId()); tempVar.setParentParemeters(getParameters()); VdcReturnValueBase result = Backend.getInstance().runInternalAction( VdcActionType.CreateSnapshotFromTemplate, tempVar); getParameters().getImagesParameters().add(tempVar); /** * if couldnt create snapshot then stop the transaction and the command */ if (!result.getSucceeded()) { throw new VdcBLLException(VdcBllErrors.IRS_IMAGE_STATUS_ILLEGAL); } else { getTaskIdList().addAll(result.getInternalTaskIdList()); } } } return true; } @Override public AuditLogType getAuditLogTypeValue() { switch (getActionState()) { case EXECUTE: return getSucceeded() ? (getReturnValue().getTaskIdList().size() > 0 ? AuditLogType.USER_ADD_VM_STARTED : AuditLogType.USER_ADD_VM) : AuditLogType.USER_FAILED_ADD_VM; case END_SUCCESS: return getSucceeded() ? AuditLogType.USER_ADD_VM_FINISHED_SUCCESS : AuditLogType.USER_ADD_VM_FINISHED_FAILURE; default: return AuditLogType.USER_ADD_VM_FINISHED_FAILURE; } } @Override protected VdcActionType getChildActionType() { return VdcActionType.CreateSnapshotFromTemplate; } @Override protected void EndWithFailure() { super.EndActionOnDisks(); if (getVm() != null) { RemoveVmInSpm(getVm().getstorage_pool_id(), getVmId()); } RemoveVmUsers(); RemoveVmNetwork(); // \\RemoveVmStatistics(); // \\RemoveVmDynamic(); RemoveVmStatic(); setSucceeded(true); } @Override public Map<Guid, VdcObjectType> getPermissionCheckSubjects() { Map<Guid, VdcObjectType> map = new HashMap<Guid, VdcObjectType>(); map.put(getVdsGroupId(), VdcObjectType.VdsGroups); map.put(getVmTemplateId(), VdcObjectType.VmTemplate); return map; } protected void addVmPermission() { if ((getParameters()).isMakeCreatorExplicitOwner()) { permissions perms = new permissions(getCurrentUser().getUserId(), PredefinedRoles.VM_OPERATOR.getId(), getVmId(), VdcObjectType.VM); MultiLevelAdministrationHandler.addPermission(perms); } } private static LogCompat log = LogFactoryCompat.getLog(AddVmCommand.class); }