package org.ovirt.engine.core.bll; import static org.ovirt.engine.core.bll.storage.disk.image.DisksFilter.ONLY_NOT_SHAREABLE; import java.util.Collection; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.inject.Inject; import org.ovirt.engine.core.bll.context.CommandContext; import org.ovirt.engine.core.bll.storage.disk.image.DisksFilter; import org.ovirt.engine.core.bll.storage.disk.image.ImagesHandler; import org.ovirt.engine.core.bll.storage.utils.BlockStorageDiscardFunctionalityHelper; import org.ovirt.engine.core.bll.tasks.interfaces.CommandCallback; import org.ovirt.engine.core.bll.validator.storage.DiskImagesValidator; import org.ovirt.engine.core.bll.validator.storage.StorageDomainValidator; import org.ovirt.engine.core.common.AuditLogType; import org.ovirt.engine.core.common.action.AddVmParameters; import org.ovirt.engine.core.common.action.CreateCloneOfTemplateParameters; import org.ovirt.engine.core.common.action.LockProperties; import org.ovirt.engine.core.common.action.LockProperties.Scope; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.businessentities.Entities; import org.ovirt.engine.core.common.businessentities.StorageDomain; import org.ovirt.engine.core.common.businessentities.StorageDomainStatus; import org.ovirt.engine.core.common.businessentities.storage.CinderDisk; import org.ovirt.engine.core.common.businessentities.storage.DiskImage; import org.ovirt.engine.core.common.businessentities.storage.DiskImageBase; import org.ovirt.engine.core.common.errors.EngineMessage; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.dao.DiskImageDao; import org.ovirt.engine.core.dao.VmStaticDao; /** * This class adds a cloned VM from a template (Deep disk copy) */ public class AddVmFromTemplateCommand<T extends AddVmParameters> extends AddVmCommand<T> { private Map<Guid, Guid> diskInfoSourceMap; private Map<Guid, Set<Guid>> validDisksDomains; @Inject private BlockStorageDiscardFunctionalityHelper discardHelper; @Inject private VmStaticDao vmStaticDao; @Inject private DiskImageDao diskImageDao; public AddVmFromTemplateCommand(T parameters, CommandContext commandContext) { super(parameters, commandContext); } protected AddVmFromTemplateCommand(Guid commandId) { super(commandId); } @Override protected LockProperties applyLockProperties(LockProperties lockProperties) { return lockProperties.withScope(Scope.Command); } @Override protected boolean validateIsImagesOnDomains() { return true; } @Override protected void init() { super.init(); T parameters = getParameters(); List<CinderDisk> cinderDisks = DisksFilter.filterCinderDisks(getVmTemplate().getDiskTemplateMap().values()); parameters.setUseCinderCommandCallback(!cinderDisks.isEmpty()); } @Override protected void executeVmCommand() { super.executeVmCommand(); getVm().setVmtGuid(VmTemplateHandler.BLANK_VM_TEMPLATE_ID); getVm().getStaticData().setQuotaId(getParameters().getVmStaticData().getQuotaId()); vmStaticDao.update(getVm().getStaticData()); checkTrustedService(); } private void checkTrustedService() { if (getVmTemplate().isTrustedService() && !getVm().isTrustedService()) { auditLogDirector.log(this, AuditLogType.USER_ADD_VM_FROM_TRUSTED_TO_UNTRUSTED); } else if (!getVmTemplate().isTrustedService() && getVm().isTrustedService()) { auditLogDirector.log(this, AuditLogType.USER_ADD_VM_FROM_UNTRUSTED_TO_TRUSTED); } } /** * TODO: need to see why those checks are not executed * for this command */ @Override protected boolean checkTemplateImages(List<String> reasons) { return true; } @Override protected VdcActionType getDiskCreationCommandType() { return VdcActionType.CreateCloneOfTemplate; } @Override protected void lockVM() { vmHandler.lockVm(getVm().getDynamicData(), getCompensationContext()); } @Override protected Collection<DiskImage> getImagesToCheckDestinationStorageDomains() { return getVmTemplate().getDiskTemplateMap().values(); } @Override protected CreateCloneOfTemplateParameters buildDiskCreationParameters(DiskImage disk) { DiskImageBase diskInfo = getParameters().getDiskInfoDestinationMap().get(disk.getId()); CreateCloneOfTemplateParameters params = new CreateCloneOfTemplateParameters(disk.getImageId(), getParameters().getVmStaticData().getId(), diskInfo); params.setStorageDomainId(diskInfoSourceMap.get(disk.getId())); params.setDestStorageDomainId(retrieveDestinationDomainForDisk(disk.getId())); params.setDiskAlias(diskInfoDestinationMap.get(disk.getId()).getDiskAlias()); params.setVmSnapshotId(getVmSnapshotId()); params.setParentCommand(VdcActionType.AddVmFromTemplate); params.setParentParameters(getParameters()); params.setEntityInfo(getParameters().getEntityInfo()); params.setQuotaId(diskInfoDestinationMap.get(disk.getId()).getQuotaId() != null ? diskInfoDestinationMap.get(disk.getId()).getQuotaId() : null); params.setDiskProfileId(diskInfoDestinationMap.get(disk.getId()).getDiskProfileId()); return params; } @Override protected boolean validate() { if (!super.validate()) { return false; } List<DiskImage> templateDiskImages = DisksFilter.filterImageDisks(getVmTemplate().getDiskTemplateMap().values(), ONLY_NOT_SHAREABLE); for (DiskImage dit : templateDiskImages) { DiskImage diskImage = diskInfoDestinationMap.get(dit.getId()); if (!ImagesHandler.checkImageConfiguration( destStorages.get(diskImage.getStorageIds().get(0)).getStorageStaticData(), diskImage, getReturnValue().getValidationMessages())) { return false; } } return true; } @Override protected boolean validateFreeSpace(StorageDomainValidator storageDomainValidator, List<DiskImage> disksList) { for (DiskImage diskImage : disksList) { List<DiskImage> snapshots = diskImageDao.getAllSnapshotsForLeaf(diskImage.getImageId()); diskImage.getSnapshots().addAll(snapshots); } return validate(storageDomainValidator.hasSpaceForClonedDisks(disksList)); } @Override protected boolean verifySourceDomains() { Map<Guid, StorageDomain> poolDomainsMap = Entities.businessEntitiesById(getPoolDomains()); EnumSet<StorageDomainStatus> validDomainStatuses = EnumSet.of(StorageDomainStatus.Active); List<DiskImage> templateDiskImages = DisksFilter.filterImageDisks(getImagesToCheckDestinationStorageDomains(), ONLY_NOT_SHAREABLE); validDisksDomains = ImagesHandler.findDomainsInApplicableStatusForDisks(templateDiskImages, poolDomainsMap, validDomainStatuses); return validate(new DiskImagesValidator(templateDiskImages).diskImagesOnAnyApplicableDomains( validDisksDomains, poolDomainsMap, EngineMessage.ACTION_TYPE_FAILED_NO_VALID_DOMAINS_STATUS_FOR_TEMPLATE_DISKS, validDomainStatuses)); } @Override protected void chooseDisksSourceDomains() { diskInfoSourceMap = new HashMap<>(); List<DiskImage> templateDiskImages = DisksFilter.filterImageDisks(getImagesToCheckDestinationStorageDomains(), ONLY_NOT_SHAREABLE); for (DiskImage disk : templateDiskImages) { Guid diskId = disk.getId(); Set<Guid> validDomainsForDisk = validDisksDomains.get(diskId); Guid destinationDomain = retrieveDestinationDomainForDisk(diskId); // if the destination domain is one of the valid source domains, we can // choose the same domain as the source domain for // possibly faster operation, otherwise we'll choose random valid domain as the source. if (validDomainsForDisk.contains(destinationDomain)) { diskInfoSourceMap.put(diskId, destinationDomain); } else { diskInfoSourceMap.put(diskId, validDomainsForDisk.iterator().next()); } } } @Override protected boolean isDisksVolumeFormatValid() { return true; } private Guid retrieveDestinationDomainForDisk(Guid id) { return diskInfoDestinationMap.get(id).getStorageIds().get(0); } @Override protected boolean isVirtioScsiEnabled() { return getParameters().isVirtioScsiEnabled() != null ? super.isVirtioScsiEnabled() : isVirtioScsiControllerAttached(getVmTemplateId()); } @Override public CommandCallback getCallback() { return new ConcurrentChildCommandsExecutionCallback(); } @Override public AuditLogType getAuditLogTypeValue() { switch (getActionState()) { case EXECUTE: return AuditLogType.USER_ADD_VM_STARTED; 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; } } }