package org.ovirt.engine.core.bll; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.ovirt.engine.core.common.AuditLogType; import org.ovirt.engine.core.common.VdcActionUtils; import org.ovirt.engine.core.common.action.CreateAllSnapshotsFromVmParameters; import org.ovirt.engine.core.common.action.RunVmParams; import org.ovirt.engine.core.common.action.VdcActionParametersBase; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.action.VdcReturnValueBase; import org.ovirt.engine.core.common.businessentities.BootSequence; import org.ovirt.engine.core.common.businessentities.DiskImage; import org.ovirt.engine.core.common.businessentities.DisplayType; import org.ovirt.engine.core.common.businessentities.FileTypeExtension; import org.ovirt.engine.core.common.businessentities.IVdcQueryable; import org.ovirt.engine.core.common.businessentities.RepoFileMetaData; import org.ovirt.engine.core.common.businessentities.StorageDomainStatus; import org.ovirt.engine.core.common.businessentities.StorageDomainType; import org.ovirt.engine.core.common.businessentities.VDS; import org.ovirt.engine.core.common.businessentities.VDSStatus; import org.ovirt.engine.core.common.businessentities.VM; import org.ovirt.engine.core.common.businessentities.VMStatus; import org.ovirt.engine.core.common.businessentities.stateless_vm_image_map; 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.GetAllIsoImagesListParameters; import org.ovirt.engine.core.common.queries.VdcQueryReturnValue; import org.ovirt.engine.core.common.queries.VdcQueryType; import org.ovirt.engine.core.common.vdscommands.CreateVmVDSCommandParameters; import org.ovirt.engine.core.common.vdscommands.IrsBaseVDSCommandParameters; import org.ovirt.engine.core.common.vdscommands.IsVmDuringInitiatingVDSCommandParameters; import org.ovirt.engine.core.common.vdscommands.ResumeVDSCommandParameters; import org.ovirt.engine.core.common.vdscommands.VDSCommandType; import org.ovirt.engine.core.common.vdscommands.VDSReturnValue; 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.StringHelper; import org.ovirt.engine.core.compat.TransactionScopeOption; import org.ovirt.engine.core.compat.Version; import org.ovirt.engine.core.compat.backendcompat.Path; import org.ovirt.engine.core.dal.VdcBllMessages; import org.ovirt.engine.core.dal.dbbroker.DbFacade; import org.ovirt.engine.core.utils.linq.LinqUtils; import org.ovirt.engine.core.utils.linq.Predicate; import org.ovirt.engine.core.utils.vmproperties.VmPropertiesUtils; public class RunVmCommand<T extends RunVmParams> extends RunVmCommandBase<T> { // this const represent the character for network in the boot sequence // options private static final char NETWORK_BOOT_SEQUENCE_CHAR = 'N'; private static final char cd = (BootSequence.D).toString().charAt(0); private String _cdImagePath = ""; private String _floppyImagePath = ""; private boolean mResume; private boolean _isVmRunningStateless; private boolean isFailedStatlessSnapshot; protected RunVmCommand(Guid commandId) { super(commandId); } public RunVmCommand(T runVmParams) { super(runVmParams); super.setVmId(runVmParams.getVmId()); getParameters().setEntityId(runVmParams.getVmId()); InitRunVmCommand(); } @Override protected VDS getDestinationVds() { if (_destinationVds == null) { Guid vdsId = getParameters().getDestinationVdsId() != null ? getParameters().getDestinationVdsId() : getVm().getdedicated_vm_for_vds() != null ? new Guid(getVm().getdedicated_vm_for_vds() .toString()) : null; if (vdsId != null) { _destinationVds = DbFacade.getInstance().getVdsDAO().get(vdsId); } } return _destinationVds; } private void InitRunVmCommand() { RunVmParams runVmParameters = getParameters(); if (!StringHelper.isNullOrEmpty(runVmParameters.getDiskPath())) { _cdImagePath = ImagesHandler.cdPathWindowsToLinux(runVmParameters.getDiskPath(), getVm() .getstorage_pool_id()); } if (!StringHelper.isNullOrEmpty(runVmParameters.getFloppyPath())) { _floppyImagePath = ImagesHandler.cdPathWindowsToLinux(runVmParameters.getFloppyPath(), getVm() .getstorage_pool_id()); } if (getVm() != null) { Guid destVdsId = (getDestinationVds() != null) ? (Guid) getDestinationVds().getvds_id() : null; setVdsSelector(new VdsSelector(getVm(), destVdsId, true)); refreshBootParameters(runVmParameters); } } /** * Refresh the associated values of the VM boot parameters with the values from the command parameters. The method * is used when VM is reloaded from the DB while its parameters hasn't been persisted (e.g. when running 'as once') * @param runVmParameters */ private void refreshBootParameters(RunVmParams runVmParameters) { // if not run once then use default boot sequence refreshBootSequenceParameter(runVmParameters); if (!StringHelper.isNullOrEmpty(runVmParameters.getinitrd_url())) { getVm().setinitrd_url(runVmParameters.getinitrd_url()); } if (!StringHelper.isNullOrEmpty(runVmParameters.getkernel_url())) { getVm().setkernel_url(runVmParameters.getkernel_url()); } if (!StringHelper.isNullOrEmpty(runVmParameters.getkernel_params())) { getVm().setkernel_params(runVmParameters.getkernel_params()); } if (!StringHelper.isNullOrEmpty(runVmParameters.getCustomProperties())) { getVm().setCustomProperties(runVmParameters.getCustomProperties()); } } private void refreshBootSequenceParameter(RunVmParams runVmParameters) { if (runVmParameters != null) { getVm().setboot_sequence(((runVmParameters.getBootSequence()) != null) ? runVmParameters.getBootSequence() : getVm().getdefault_boot_sequence()); } } /** * Returns the full path file name of Iso file. If the Iso file has prefix of Iso://, then we set the prefix to the * path of the domain on the Iso Domain server<BR/> * otherwise, returns the original name.<BR/> * Note: The prefix is not case sensitive. * * @param url * - String of the file url. ("iso://initrd.ini" or "/init/initrd.ini". * @return String of the full file path. */ private String getIsoPrefixFilePath(String url) { String isoPrefixName = "iso://"; // The initial Url. String fullPathFileName = url; // If file name got prefix of iso:// then set the path to the Iso domain. int prefixLength = isoPrefixName.length(); if (url.length() >= prefixLength && (url.substring(0, prefixLength)).equalsIgnoreCase(isoPrefixName)) { fullPathFileName = ImagesHandler.cdPathWindowsToLinux(url.substring(prefixLength, url.length()), getVm() .getstorage_pool_id()); } return fullPathFileName; } private void ResumeVm() { mResume = true; // Vds = ResourceManager.Instance.getVds(Vm.run_on_vds.Value); setVdsId(new Guid(getVm().getrun_on_vds().toString())); if (getVds() != null) { try { IncrementVdsPendingVmsCount(); VDSReturnValue result = Backend .getInstance() .getResourceManager() .RunAsyncVdsCommand(VDSCommandType.Resume, new ResumeVDSCommandParameters(getVdsId(), getVm().getvm_guid()), this); setActionReturnValue(result.getReturnValue()); setSucceeded(result.getSucceeded()); } finally { DecrementVdsPendingVmsCount(); } } else { setActionReturnValue(getVm().getstatus()); } } protected void RunVm() { setActionReturnValue(VMStatus.Down); if (GetVdsToRunOn()) { VMStatus status; try { IncrementVdsPendingVmsCount(); AttachCd(); status = CreateVm(); } finally { DecrementVdsPendingVmsCount(); } setActionReturnValue(status); if (VM.isStatusUp(status) || status == VMStatus.RestoringState) { setSucceeded(true); } else { // Try to rerun Vm on different vds // no need to log the command because it is being logged inside // the rerun log.infoFormat("Failed to run desktop {0}, rerun", getVm().getvm_name()); setCommandShouldBeLogged(false); setSucceeded(true); Rerun(); } } else { FailedToRunVm(); setSucceeded(false); _isRerun = false; } } @Override protected void ExecuteVmCommand() { setActionReturnValue(VMStatus.Down); if (InitVm()) { if (getVm().getstatus() == VMStatus.Paused) { // resume ResumeVm(); } else { // run vm if (!_isRerun && Boolean.TRUE.equals(getParameters().getRunAsStateless()) && !getVm().getDiskList().isEmpty() && getVm().getstatus() != VMStatus.Suspended) { StatelessVmTreatment(); } else if (!getParameters().getIsInternal() && !_isRerun && getVm().getstatus() != VMStatus.Suspended && DbFacade.getInstance() .getDiskImageDAO() .getAllStatelessVmImageMapsForVm(getVm().getvm_guid()) .size() > 0) { removeVmStatlessImages(); } else { RunVm(); } } } else { setActionReturnValue(getVm().getstatus()); } } /** * Handles the cd attachment. Set the VM CDPath to the ISO Path stored in the database (default) Call * GuestToolsVersionTreatment to override CDPath by guest tools if needed. If the CD symbol ('C') is contained in * the Boot Sequence (at any place) set again the CDPath to the ISO Path that was stored in the database, and we * assume that this CD is bootable. So , priorities are (from low to high) : 1)Default 2)Tools 3)Boot Sequence */ private void AttachCd() { Guid storagePoolId = getVm().getstorage_pool_id(); boolean isIsoFound = (findActiveISODomain(storagePoolId) != null); if (isIsoFound) { if (StringHelper.isNullOrEmpty(getVm().getCdPath())) { getVm().setCdPath(getVm().getiso_path()); GuestToolsVersionTreatment(); // LINQ Vm.boot_sequence.toString().Contains(cd)) refreshBootSequenceParameter(getParameters()); if (getVm().getboot_sequence().toString().indexOf(cd) > -1) { getVm().setCdPath(getVm().getiso_path()); } getVm().setCdPath(ImagesHandler.cdPathWindowsToLinux(getVm().getCdPath(), getVm().getstorage_pool_id())); } } else if (!StringHelper.isNullOrEmpty(getVm().getiso_path())) { getVm().setCdPath(""); log.error("Can not attach CD without active ISO domain"); } } private void StatelessVmTreatment() { /** * if one of vm's images is in the DB dont do anything. */ if (DbFacade.getInstance().getDiskImageDAO().getAllStatelessVmImageMapsForVm(getVm().getvm_guid()).size() == 0) { log.infoFormat("VdcBll.RunVmCommand.RunVmAsStateless - Creating snapshot for stateless vm {0} - {1}", getVm().getvm_name(), getVm().getvm_guid()); lockVmWithCompensationIfNeeded(); CreateAllSnapshotsFromVmParameters tempVar = new CreateAllSnapshotsFromVmParameters(getVm().getvm_guid(), "stateless snapshot"); tempVar.setShouldBeLogged(false); tempVar.setParentCommand(VdcActionType.RunVm); tempVar.setEntityId(getParameters().getEntityId()); CreateAllSnapshotsFromVmParameters p = tempVar; VdcReturnValueBase vdcReturnValue = Backend.getInstance().runInternalAction( VdcActionType.CreateAllSnapshotsFromVm, p, getCompensationContext()); setSucceeded(vdcReturnValue.getSucceeded()); if (vdcReturnValue.getSucceeded()) { // saving all vm images in order to return to them (not using // RestoreAllSnapshots) for (DiskImage disk : getVm().getDiskMap().values()) { /** * add new stateless vm image to db */ DbFacade.getInstance().getDiskImageDAO().addStatelessVmImageMap( new stateless_vm_image_map(disk.getId(), disk.getinternal_drive_mapping(), getVm() .getvm_guid())); } getParameters().getImagesParameters().add(p); getReturnValue().getTaskIdList().addAll(vdcReturnValue.getInternalTaskIdList()); // save RunVmParams so we'll know how to run // the stateless VM in the EndAction part. VmHandler.updateDisksFromDb(getVm()); } else { if (vdcReturnValue.getCanDoActionMessages().contains( VdcBllMessages.ACTION_TYPE_FAILED_VM_IMAGE_IS_LOCKED)) { throw new VdcBLLException(VdcBllErrors.IRS_IMAGE_STATUS_ILLEGAL); } else { getReturnValue().setFault(vdcReturnValue.getFault()); } log.errorFormat("RunVmAsStateless - {0} - failed to create snapshots", getVm().getvm_name()); } } else { log.errorFormat( "RunVmAsStateless - {0} - found existing vm images in stateless_vm_image_map table - skipped creating snapshots.", getVm().getvm_name()); removeVmStatlessImages(); } } private void removeVmStatlessImages() { isFailedStatlessSnapshot = true; VmPoolHandler.removeVmStatelessImages(getVm().getvm_guid()); setSucceeded(true); } private void DecrementVdsPendingVmsCount() { synchronized (_vds_pending_vm_count) { int i = _vds_pending_vm_count.get(getVdsId()); _vds_pending_vm_count.put(getVdsId(), i - 1); } } private void IncrementVdsPendingVmsCount() { synchronized (_vds_pending_vm_count) { if (!_vds_pending_vm_count.containsKey(getVdsId())) { _vds_pending_vm_count.put(getVdsId(), 1); } else { int i = _vds_pending_vm_count.get(getVdsId()); _vds_pending_vm_count.put(getVdsId(), i + 1); } } } protected VMStatus CreateVm() { // reevaluate boot parameters if VM was executed with 'run once' refreshBootParameters(getParameters()); // Set path for initrd and kernel image. if (!StringHelper.isNullOrEmpty(getVm().getinitrd_url())) { getVm().setinitrd_url(getIsoPrefixFilePath(getVm().getinitrd_url())); } if (!StringHelper.isNullOrEmpty(getVm().getkernel_url())) { getVm().setkernel_url(getIsoPrefixFilePath(getVm().getkernel_url())); } return (VMStatus) Backend .getInstance() .getResourceManager() .RunAsyncVdsCommand(VDSCommandType.CreateVm, initVdsCreateVmParams(), this).getReturnValue(); } /** * Initial the parameters for the VDSM command for VM creation * @return the VDS create VM parameters */ protected CreateVmVDSCommandParameters initVdsCreateVmParams() { return new CreateVmVDSCommandParameters(getVdsId(), getVm()); } @Override public AuditLogType getAuditLogTypeValue() { switch (getActionState()) { case EXECUTE: if(isFailedStatlessSnapshot) { return AuditLogType.USER_RUN_VM_FAILURE_STATELESS_SNAPSHOT_LEFT; } if (mResume) { return getSucceeded() ? AuditLogType.USER_RESUME_VM : AuditLogType.USER_FAILED_RESUME_VM; } else if (getParameters() != null && getParameters().getIsInternal()) { return getSucceeded() ? AuditLogType.VDS_INITIATED_RUN_VM : AuditLogType.VDS_INITIATED_RUN_VM_FAILED; } else { return getSucceeded() ? (VMStatus) getActionReturnValue() == VMStatus.Up ? (getParameters() != null && getParameters().getDestinationVdsId() == null && getVm().getdedicated_vm_for_vds() != null && !getVm() .getrun_on_vds().equals(getVm().getdedicated_vm_for_vds())) ? AuditLogType.USER_RUN_VM_ON_NON_DEFAULT_VDS : AuditLogType.USER_RUN_VM : _isRerun ? AuditLogType.VDS_INITIATED_RUN_VM : getTaskIdList().size() > 0 ? AuditLogType.USER_INITIATED_RUN_VM : AuditLogType.USER_STARTED_VM : _isRerun ? AuditLogType.USER_INITIATED_RUN_VM_FAILED : AuditLogType.USER_FAILED_RUN_VM; } case END_SUCCESS: // if not running as stateless, or if succeeded running as // stateless, // command should be with 'CommandShouldBeLogged = false': return _isVmRunningStateless && !getSucceeded() ? AuditLogType.USER_RUN_VM_AS_STATELESS_FINISHED_FAILURE : AuditLogType.UNASSIGNED; case END_FAILURE: // if not running as stateless, command should // be with 'CommandShouldBeLogged = false': return _isVmRunningStateless ? AuditLogType.USER_RUN_VM_AS_STATELESS_FINISHED_FAILURE : AuditLogType.UNASSIGNED; default: // all other cases should be with 'CommandShouldBeLogged = // false': return AuditLogType.UNASSIGNED; } } protected boolean InitVm() { if (getVm() == null) { log.warnFormat("ResourceManager::{0}::No such vm (where id = '{1}' )in database", getClass().getName(), getVmId().toString()); throw new VdcBLLException(VdcBllErrors.DB_NO_SUCH_VM); } if ((getVm().getstatus() == VMStatus.ImageIllegal) || (getVm().getstatus() == VMStatus.ImageLocked)) { log.warnFormat("ResourceManager::{0}::vm '{1}' has {2}", getClass().getName(), getVmId().toString(), (getVm().getstatus() == VMStatus.ImageLocked ? "a locked image" : "an illegal image")); setActionReturnValue(getVm().getstatus()); return false; } else { HandleMemoryAdjustments(); VmHandler.updateDisksFromDb(getVm()); getVm().setCdPath(_cdImagePath); getVm().setFloppyPath(_floppyImagePath); getVm().setkvm_enable(getParameters().getKvmEnable()); getVm().setRunAndPause(getParameters().getRunAndPause()); getVm().setacpi_enable(getParameters().getAcpiEnable()); getParameters().setRunAsStateless(shouldVmRunAsStateless(getParameters(), getVm())); // if Use Vnc is null it means runVM was launch not from the run // once command if (getParameters().getUseVnc() != null) { getVm().setdisplay_type(getParameters().getUseVnc() ? DisplayType.vnc : DisplayType.qxl); } else { getVm().setdisplay_type(getVm().getdefault_display_type()); } if (getParameters().getReinitialize()) { getVm().setUseSysPrep(true); } // if we attach floppy we don't need the sysprep if (!StringHelper.isNullOrEmpty(getVm().getFloppyPath())) { DbFacade.getInstance().getVmStaticDAO().update(getVm().getStaticData()); } // get what cpu flags should be passed to vdsm according to cluster // cpu name getVm().setvds_group_cpu_flags_data( CpuFlagsManagerHandler.GetVDSVerbDataByCpuName(getVm().getvds_group_cpu_name(), getVm() .getvds_group_compatibility_version())); return true; } } protected void HandleMemoryAdjustments() { // nothing to do in RunVmCommand class } protected boolean GetVdsToRunOn() { // use destination vds or default vds or none setVdsId(getVdsSelector().GetVdsToRunOn()); setVds(null); setVdsName(null); if (getVdsId().equals(Guid.Empty)) { log.errorFormat("Can't find VDS to run the VM {0} on, so this VM will not be run.", getVmId()); return false; } if (getVds() == null) { VdcBLLException outEx = new VdcBLLException(VdcBllErrors.RESOURCE_MANAGER_VDS_NOT_FOUND); log.error(String.format("VmHandler::%1$s", getClass().getName()), outEx); return false; } return true; } /** * If vdss version greater then vm's and vm not running with cd and there is appropriate QumranetAgentTools image - * add it to vm as cd. */ private void GuestToolsVersionTreatment() { boolean attachCd = false; String selectedToolsVersion = ""; String selectedToolsClusterVersion = ""; VmHandler.UpdateVmGuestAgentVersion(getVm()); storage_domains isoDomain = null; if (!getVm().getvm_os().isLinux() && (null != (isoDomain = LinqUtils.firstOrNull( DbFacade.getInstance() .getStorageDomainDAO() .getAllForStoragePool(getVm().getstorage_pool_id()), new Predicate<storage_domains>() { @Override public boolean eval(storage_domains domain) { return domain.getstorage_domain_type() == StorageDomainType.ISO; } })) && isoDomain.getstatus() == StorageDomainStatus.Active && StringHelper .isNullOrEmpty(_cdImagePath))) { // get cluster version of the vm tools Version vmToolsClusterVersion = null; if (getVm().getHasAgent()) { Version clusterVer = getVm().getPartialVersion(); if (Version.OpEquality(clusterVer, new Version("4.4"))) { vmToolsClusterVersion = new Version("2.1"); } else { vmToolsClusterVersion = clusterVer; } } // Fetch cached Iso files from active Iso domain. List<RepoFileMetaData> repoFilesMap = IsoDomainListSyncronizer.getInstance().getCachedIsoListByDomainId(isoDomain.getid(), FileTypeExtension.ISO); Version bestClusterVer = null; int bestToolVer = 0; for (RepoFileMetaData map : repoFilesMap) { String fileName = map.getRepoFileName() != null ? map.getRepoFileName() : ""; Matcher matchToolPattern = Pattern.compile(IsoDomainListSyncronizer.regexToolPattern).matcher(fileName); if (matchToolPattern.find()) { // Get cluster version and tool version of Iso tool. // TODO: Should be group name string support in java7. Version clusterVer = new Version(matchToolPattern.group(1)); int toolVersion = Integer.parseInt(matchToolPattern.group(3)); if (clusterVer.compareTo(getVm().getvds_group_compatibility_version()) <= 0) { if ((bestClusterVer == null) || (clusterVer.compareTo(bestClusterVer) > 0)) { bestToolVer = toolVersion; bestClusterVer = clusterVer; } else if ((Version.OpEquality(clusterVer, bestClusterVer)) && (toolVersion > bestToolVer)) { bestToolVer = toolVersion; bestClusterVer = clusterVer; } } } } if (bestClusterVer != null && (vmToolsClusterVersion == null || vmToolsClusterVersion.compareTo(bestClusterVer) < 0 || (Version .OpEquality(vmToolsClusterVersion, bestClusterVer) && getVm().getHasAgent() && getVm().getGuestAgentVersion().getBuild() < bestToolVer))) { // Vm has no tools or there are new tools selectedToolsVersion = (Integer.toString(bestToolVer)); selectedToolsClusterVersion = bestClusterVer.toString(); attachCd = true; } } if (attachCd) { // if minimalVdsRev isn't empty use new iso files name convention // string qumranetToolsPath = minimalVdsRev == string.Empty // ? // string.Format("{0}{1}.iso", GuestToolsSetupIsoPrefix, revision) // : // // format is RHEV-ToolsSetup_tools_ver_vds_min_ver // string.Format("{0}{1}_{2}.iso", GuestToolsSetupIsoPrefix, // revision, // minimalVdsRev); String qumranetToolsPath = String.format("%1$s%2$s_%3$s.iso", IsoDomainListSyncronizer.guestToolsSetupIsoPrefix, selectedToolsClusterVersion, selectedToolsVersion); String isoDir = (String) Backend .getInstance() .getResourceManager() .RunVdsCommand(VDSCommandType.IsoDirectory, new IrsBaseVDSCommandParameters(getVm().getstorage_pool_id())).getReturnValue(); qumranetToolsPath = Path.Combine(isoDir, qumranetToolsPath); getVm().setCdPath(ImagesHandler.cdPathWindowsToLinux(qumranetToolsPath, getVm().getstorage_pool_id())); } } public boolean CanRunVm() { return CanRunVm(getVm(), getReturnValue().getCanDoActionMessages(), getParameters(), getVdsSelector()); } public static boolean CanRunVm(VM vm, java.util.ArrayList<String> message, RunVmParams runParams, VdsSelector vdsSelector) { boolean retValue = true; List<VmPropertiesUtils.ValidationError> validationErrors = null; if (vm == null) { retValue = false; if (message != null) { message.add(VdcBllMessages.ACTION_TYPE_FAILED_VM_NOT_FOUND.toString()); } } else if (!(validationErrors = VmPropertiesUtils.validateVMProperties(vm.getStaticData())).isEmpty()) { handleCustomPropertiesError(validationErrors, message); retValue = false; } else { BootSequence boot_sequence = ((runParams.getBootSequence()) != null) ? runParams.getBootSequence() : vm .getdefault_boot_sequence(); List<DiskImage> vmImages = DbFacade.getInstance().getDiskImageDAO().getAllForVm(vm.getvm_guid()); Guid storagePoolId = vm.getstorage_pool_id(); // Block from running a VM with no HDD when its first boot device is // HD // and no other boot devices are configured if (vmImages.isEmpty() && StringHelper.EqOp(boot_sequence.toString(), BootSequence.C.toString())) { message.add(VdcBllMessages.VM_CANNOT_RUN_FROM_DISK_WITHOUT_DISK.toString()); retValue = false; } else { // If CD appears as first and there is no ISO in storage // pool/ISO inactive - // you cannot run this VM if ((findActiveISODomain(storagePoolId) == null) && boot_sequence.toString().length() > 0 && (boot_sequence.toString().charAt(0) == cd)) { message.add(VdcBllMessages.VM_CANNOT_RUN_FROM_CD_WITHOUT_ACTIVE_STORAGE_DOMAIN_ISO.toString()); retValue = false; } // custom properties allowed only from cluster 2.3 else if (!StringHelper.isNullOrEmpty(vm.getStaticData().getCustomProperties()) && !Config.<Boolean> GetValue(ConfigValues.SupportCustomProperties, vm.getvds_group_compatibility_version().getValue())) { message.add(VdcBllMessages.CUSTOM_VM_PROPERTIES_INVALID_VALUES_NOT_ALLOWED_IN_CURRENT_CLUSTER.toString()); retValue = false; } else { // if there is network in the boot sequence, check that the // vm has network, // otherwise the vm cannot be run in vdsm if (boot_sequence.toString().indexOf(NETWORK_BOOT_SEQUENCE_CHAR) > -1 && DbFacade.getInstance().getVmNetworkInterfaceDAO().getAllForVm(vm.getvm_guid()).size() == 0) { message.add(VdcBllMessages.VM_CANNOT_RUN_FROM_NETWORK_WITHOUT_NETWORK.toString()); retValue = false; } else { if (vmImages.size() > 0) { Guid storageDomainId = vmImages.get(0).getstorage_id().getValue(); // check isValid, storageDomain and diskSpace only // if VM is not HA VM if (!ImagesHandler .PerformImagesChecks(vm.getvm_guid(), message, vm.getstorage_pool_id(), storageDomainId, !vm.getauto_startup(), true, false, false, false, false, !vm.getauto_startup() && !storageDomainId.equals(Guid.Empty) || !runParams.getIsInternal() && vm.getauto_startup(), !vm.getauto_startup() || !runParams.getIsInternal() && vm.getauto_startup())) { retValue = false; } // Check if iso and floppy path exists else if (!vm.getauto_startup() && !validateIsoPath(findActiveISODomain(vm.getstorage_pool_id()), runParams, message)) { retValue = false; } else { boolean isVmDuringInit = ((Boolean) Backend .getInstance() .getResourceManager() .RunVdsCommand(VDSCommandType.IsVmDuringInitiating, new IsVmDuringInitiatingVDSCommandParameters(vm.getvm_guid())) .getReturnValue()).booleanValue(); if (vm.isStatusUp() || (vm.getstatus() == VMStatus.NotResponding) || isVmDuringInit) { retValue = false; if (message != null) { message.add(VdcBllMessages.ACTION_TYPE_FAILED_VM_IS_RUNNING.toString()); } } else if (vm.getstatus() == VMStatus.Paused && vm.getrun_on_vds() != null) { VDS vds = DbFacade.getInstance().getVdsDAO().get( new Guid(vm.getrun_on_vds().toString())); if (vds.getstatus() != VDSStatus.Up) { retValue = false; if (message != null) { message.add(VdcBllMessages.ACTION_TYPE_FAILED_VDS_STATUS_ILLEGAL.toString()); } } } boolean isStatelessVm = shouldVmRunAsStateless(runParams, vm); if (retValue && isStatelessVm && ImagesHandler.isVmInPreview(vmImages)) { retValue = false; message.add(VdcBllMessages.VM_CANNOT_RUN_STATELESS_WHILE_IN_PREVIEW.toString()); } // if the VM itself is stateless or run once as stateless if (retValue && isStatelessVm && vm.getauto_startup()) { retValue = false; message.add(VdcBllMessages.VM_CANNOT_RUN_STATELESS_HA.toString()); } retValue = retValue == false ? retValue : vdsSelector.CanFindVdsToRunOn(message, false); /** * only if can do action ok then check with actions matrix that status is valid for this * action */ if (retValue && !VdcActionUtils.CanExecute(new java.util.ArrayList<IVdcQueryable>( java.util.Arrays.asList(new IVdcQueryable[] { vm })), VM.class, VdcActionType.RunVm)) { message.add(VdcBllMessages.ACTION_TYPE_FAILED_VM_STATUS_ILLEGAL.toString()); retValue = false; } } } } } } } return retValue; } @SuppressWarnings("unchecked") protected static boolean validateIsoPath(Guid storageDomainId, RunVmParams runParams, java.util.ArrayList<String> messages) { if (!StringHelper.isNullOrEmpty(runParams.getDiskPath())) { if (storageDomainId == null) { messages.add(VdcBllMessages.VM_CANNOT_RUN_FROM_CD_WITHOUT_ACTIVE_STORAGE_DOMAIN_ISO.toString()); return false; } boolean retValForIso = false; VdcQueryReturnValue ret = Backend.getInstance().runInternalQuery( VdcQueryType.GetAllIsoImagesList, new GetAllIsoImagesListParameters(storageDomainId)); if (ret != null && ret.getReturnValue() != null && ret.getSucceeded()) { List<RepoFileMetaData> repoFileNameList = (List<RepoFileMetaData>) ret.getReturnValue(); if (repoFileNameList != null) { for (RepoFileMetaData isoFileMetaData : (List<RepoFileMetaData>) ret.getReturnValue()) { if (isoFileMetaData.getRepoFileName().equals(runParams.getDiskPath())) { retValForIso = true; break; } } } } if (!retValForIso) { messages.add(VdcBllMessages.ERROR_CANNOT_FIND_ISO_IMAGE_PATH.toString()); return false; } } if (!StringHelper.isNullOrEmpty(runParams.getFloppyPath())) { boolean retValForFloppy = false; VdcQueryReturnValue ret = Backend.getInstance().runInternalQuery( VdcQueryType.GetAllFloppyImagesList, new GetAllIsoImagesListParameters(storageDomainId)); if (ret != null && ret.getReturnValue() != null && ret.getSucceeded()) { List<RepoFileMetaData> repoFileNameList = (List<RepoFileMetaData>) ret.getReturnValue(); if (repoFileNameList != null) { for (RepoFileMetaData isoFileMetaData : (List<RepoFileMetaData>) ret.getReturnValue()) { if (isoFileMetaData.getRepoFileName().equals(runParams.getFloppyPath())) { retValForFloppy = true; break; } } } } if (!retValForFloppy) { messages.add(VdcBllMessages.ERROR_CANNOT_FIND_FLOPPY_IMAGE_PATH.toString()); return false; } } return true; } @Override protected boolean canDoAction() { // setting the RunVmParams Internal flag according to the command Internal flag. // we can not use only the command Internal flag and remove this flag from RunVmParams // since canRunVm is static and can not call non-static method isInternalExecution getParameters().setIsInternal(isInternalExecution()); boolean canDoAction = CanRunVm(); if (!canDoAction) { addCanDoActionMessage(VdcBllMessages.VAR__ACTION__RUN); addCanDoActionMessage(VdcBllMessages.VAR__TYPE__VM); } return canDoAction; } @Override protected void EndSuccessfully() { SetIsVmRunningStateless(); if (_isVmRunningStateless) { if (DbFacade.getInstance().getDiskImageDAO().getAllStatelessVmImageMapsForVm(getVmId()).size() > 0) { VdcActionParametersBase createSnapshotParameters = getParameters().getImagesParameters().get(0); if (createSnapshotParameters != null) { createSnapshotParameters.setTransactionScopeOption(TransactionScopeOption.RequiresNew); } VdcReturnValueBase vdcReturnValue = Backend.getInstance().EndAction( VdcActionType.CreateAllSnapshotsFromVm, createSnapshotParameters); getParameters().setShouldBeLogged(false); getParameters().setRunAsStateless(false); getParameters().setIsInternal(true); setSucceeded(Backend.getInstance().runInternalAction(VdcActionType.RunVm, getParameters()) .getSucceeded()); if (!getSucceeded()) { // could not run the vm don't try to run the end action // again log.warnFormat("Could not run the vm {0} on RunVm.EndSuccessfully", getVm().getvm_name()); getReturnValue().setEndActionTryAgain(false); } } else // the stateless-snapshot no longer exists (probably due to // ProcessVmPoolOnStopVm // treatment) -> no point in running the VM or retrying to // EndAction: { getReturnValue().setEndActionTryAgain(false); } } /** * Hibernation (VMStatus.Suspended) treatment: */ else { super.EndSuccessfully(); } } @Override protected void EndWithFailure() { SetIsVmRunningStateless(); if (_isVmRunningStateless) { VdcReturnValueBase vdcReturnValue = Backend.getInstance().endAction(VdcActionType.CreateAllSnapshotsFromVm, getParameters().getImagesParameters().get(0), getCompensationContext()); if (getVm() != null) { VmHandler.updateDisksFromDb(getVm()); for (DiskImage disk : getVm().getDiskMap().values()) { /** * remove stateless vm image from db: */ DbFacade.getInstance().getDiskImageDAO().removeStatelessVmImageMap(disk.getId()); } } else { setCommandShouldBeLogged(false); log.warn("RunVmCommand::EndWithFailure [stateless]: Vm is null - not performing full EndAction"); } setSucceeded(vdcReturnValue.getSucceeded()); // we are not running the VM, of course, // since we couldn't create a snpashot. } else { super.EndWithFailure(); } } private void SetIsVmRunningStateless() { List<stateless_vm_image_map> list = DbFacade.getInstance().getDiskImageDAO().getAllStatelessVmImageMapsForVm( getVmId()); _isVmRunningStateless = (list != null && list.size() > 0); } /** * Checks if there is an active ISO domain in the storage pool. If so returns the Iso Guid, otherwise returns null. * * @param storagePoolId * The storage pool id. * @return Iso Guid of active Iso, and null if not. */ public static Guid findActiveISODomain(Guid storagePoolId) { Guid isoGuid = null; List<storage_domains> domains = DbFacade.getInstance().getStorageDomainDAO().getAllForStoragePool( storagePoolId); for (storage_domains domain : domains) { if (domain.getstorage_domain_type() == StorageDomainType.ISO) { storage_domains sd = DbFacade.getInstance().getStorageDomainDAO().getForStoragePool(domain.getid(), storagePoolId); if (sd != null && sd.getstatus() == StorageDomainStatus.Active) { isoGuid = sd.getid(); break; } } } return isoGuid; } private static boolean shouldVmRunAsStateless(RunVmParams param, VM vm) { if (param.getRunAsStateless() != null) { return param.getRunAsStateless(); } return vm.getis_stateless(); } private static LogCompat log = LogFactoryCompat.getLog(RunVmCommand.class); }